题目:http://acm.hdu.edu.cn/showproblem.php?pid=1142
题意:这题的题意很重要,起点是1,终点是2,现在主人公已经知道,所有边的长度,然后他开始选择走不走某条边了。。
对于图中的每条边(A,B),他选这条边的前提是:存在一条B到终点的路径,比所有从A道终点的路径都短(即只要B到2的最短路径比A到2的最短路径短就存在了),选择完所有可以走的边后,问一共有多少条从1到2的路径
因为对于每条边(A,B)都要知道A到2的最短路和B到2的最短路,对于每个点都用Dijkstra的话O(n^3)超时,注意到,每次都是和2的最短路,所以灵活点,以2为起点用以次Dijkstra就可以求出所有结果。
然后用深搜,搜出所有的边,但是会超时,所以要用到记忆化搜索,要用辅助数组来保存结果,ans[now]表示now到终点一共有多少条路。开始初始化为0。。。。。由于某个点,可能有多条路经过,用了记忆化搜索,当不是第一次经过的时候,ans[now]已经有答案了。。。。要特别注意的是这里其实根本不需要用vis[]来标记有每走过,ans[now]已经记录了有每走过了。。。。。。。这里跟Dfs中for里vis[]区分开来。。。。。
记忆化搜索是为了避免重复做同一件事(这题是为了避免重复计算某点到终点的路径数)
而,深搜for里的vis[]是为了当从另一条路经过该点时能够经过。。
代码:
#include <iostream> #include <vector> using namespace std; const int M = 1111; const int INF = 1 << 30; int d[M]; int g[M][M]; bool used[M]; //bool vis[M]; int ans[M]; int compare; int n, m; //vector <int> gg[1010]; void D(int star)//记录所有到2的最短路径 { for (int i = 1; i <= n; i++) { d[i] = INF; } memset(used, 0, sizeof(used)); d[star] = 0; for (int cnt = 0; cnt < n; cnt++) { int min = INF; int min_num = 0; for (int i = 1; i <= n; i++) { if (!used[i] && min > d[i]) { min = d[i]; min_num = i; } } used[min_num] = 1; for (int i = 1; i <= n; i++) { if (!used[i] && d[i] > d[min_num] + g[min_num][i]) { d[i] = d[min_num] + g[min_num][i]; } } } } int Dfs(int now) { if (ans[now]) { return ans[now]; } if (now == 2) { return 1; } for (int i = 1; i <= n; i++) { if (/*!vis[i] && */g[now][i] != INF && d[now] > d[i]) { ans[now] += Dfs(i); } } return ans[now]; } int main() { while (scanf("%d", &n), n) { for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { g[i][j] = INF; } } scanf("%d", &m); while (m--) { int a, b, c; scanf("%d%d%d", &a, &b, &c); g[a][b] = g[b][a] = c; } D(2); memset(ans, 0, sizeof(ans));//ans[i]表示i道终点路径的条数 //memset(vis, 0, sizeof(vis)); //vis[1] = 1; printf("%d\n", Dfs(1)); } return 0; }