这道题目我一开始的想法是还是按照保存父节点的基本思路,但是每次的时候需要挑选每个节点中权值最小的那条边对应的那个点
但是这样会有问题:
第一个问题是:如果某个节点有两条边的权值都是一样的,那么这两个对应的点都必须要入队,那么问题就出现了,如果这俩个节点对应的下一个节点是同一个节点,但是先入队的那个的对应边的权值比较大,后入队的那条边对应的权值比较小,那么在前一个节点访问设置vis后,后面那个节点就不可以再访问了。
其实上面这个情况还是最简单的情况,还有很多更复杂的情况没有考虑到。
所以需要使用刘汝佳的方式:
第一步,先逆向BFS,将每一个点到终点的距离都找到,并放入d[]数组当中;这样一来,每一条最短路径在d数组中对应的距离都是按照-1开始递减的。
第二步,从起点开始BFS,每次都寻找d中比当前点小1的那个点,并且要边的权值最小,如果有多条边的权值都是最小值,那么这几个点需要一起判断。
下面重点来讲解一下第二步的实现方式:
从起点开始,先遍历一下所有d比起点小1的那些点,找到最小的权值的边,然后用这个权值来寻找对应的点,并将这些点放入next《vector》中,这个数组中的点就是下次需要判断的点。所以需要设立两个next《vector》.中途可以将最短的权值 的边保存起来,便于输出。
要注意的是,必须要遍历两次,因为第一次遍历的时候并不知道权值最小的边是哪一条。第二点是,第二次遍历的时候需要设置vis,虽然只有最短边的d数组是符合条件的,所以不加vis也是符合要求的,但是考虑一种情况,那就是两个点对应的下一个点是同一个点,那么这样同一个点就会进入next《vector》两次,而实际上只有需要进入一次。所以vis不仅可以保证正确性,还可以保证访问的效率。
其次有必要说一说这个题目中边的保存方式:
一改往日的数组保存,在这种节点特别多的情况下,将每一个点的边(结构体)保存到G[i]中,也是一个不错的选择
最后来说说vc的使用:
今天发现了两个,第一个是min函数需要变成__min, long long 需要变成__int64;
还有就是并列的for循环,如果在一开始定义变量的话(int;;),那么是不可以重复的。
下面附上AC代码
//这题就是最短路径的变形,主要有两点 //第一点就是在两个点有多条道路的时候,选一条最小的 //第二点就是最后可能有多条路径,从其中选择字典序最小的很容易,但是我怎么知道那一条是最短的呢? //明白了,一条一条比较就ok //上面这种方式可能比较的麻烦一点,首先记录父节点就是一个麻烦事//而且记录完父节点还需要去把所有的最短路都计算一遍 //最后的比较也不怎么好搞,因为是倒着比较的,这吉尔太麻烦了,其实这两种方式都可以,我觉得我的方式更具有普遍性一点 //从终点倒着BFS求最短路径的时候,一定是和正着的时候一样的,因为最短路径的长度是不变的。 //此题刘汝佳的解法不怎么具有普遍性,因为它只能保存边,而不能得到点,如果想要得到点也可以再继续计算一下就可以了 //但它的这个next的方式非常的好 //保存图的方式也有了变化,不再使用数组保存,而开始使用结构体了,所有的边都保存在一个vector中,然后每一个g[vec]中保存边的编号 #include<cstdio> #include<iostream> #include<queue> #include<algorithm> #include<vector> #include<cstring> using namespace std; const int maxn = 100000 + 10; int num_v,num_e; struct edge { int v,c; edge(int a = 0, int b = 0):v(a),c(b){} }; vector<edge>G[maxn]; int vis[maxn]; int d[maxn]; int main() { #ifdef local freopen("input.txt","r",stdin); #endif while(scanf("%d%d",&num_v,&num_e) == 2) { for(int i_0 = 1; i_0 <= num_v; i_0++) G[i_0].clear(); memset(vis,0,sizeof(vis)); memset(d,0,sizeof(d)); for(int i_1 = 1; i_1 <= num_e; i_1++) { int a,b,c; scanf("%d%d%d",&a,&b,&c); G[a].push_back(edge(b,c)); G[b].push_back(edge(a,c)); } queue<int>Q; Q.push(num_v); vis[num_v] = 1; d[num_v] = 0; //d[]保存所有的距离,但并不是每一个点都有这个距离 while(!Q.empty()) { int x = Q.front(); if(x == 1) break; Q.pop(); for(int i_2 = 0; i_2 < G[x].size();i_2++) { int v = G[x][i_2].v; if(!vis[v]) { vis[v] = 1; d[v] = d[x] + 1; Q.push(v); } } } memset(vis ,0,sizeof(vis)); vis[1] = 1; vector<int>next; vector<int>color; next.push_back(1); for(int i_2 = 1;i_2 <= d[1];i_2++) { int min_color = 1000000000; for(int j = 0; j < next.size();j++) { int x = next[j]; for(int j_2 = 0;j_2 < G[x].size();j_2++) { if(d[G[x][j_2].v] == d[x] - 1) { min_color = __min(min_color,G[x][j_2].c); } } } color.push_back(min_color); vector<int>next2; for(int j_1 = 0;j_1 < next.size();j_1++) { int x = next[j_1]; for(int j_2 = 0;j_2 < G[x].size();j_2++) { if(d[G[x][j_2].v] == d[x] - 1 && G[x][j_2].c == min_color && !vis[G[x][j_2].v])//就是不知道应不应该判断vis??? { vis[G[x][j_2].v] = 1;//判重的作用不仅是为了提高正确率,还可以减少无谓的时间消耗 next2.push_back(G[x][j_2].v); } } } next = next2; } printf("%d\n",d[1]); for(int i = 0; i < color.size();i++) { if(i) printf(" "); printf("%d",color[i] ); } printf("\n"); } return 0; }
原文地址:https://www.cnblogs.com/TorettoRui/p/10459790.html