http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1456
题目大意:有N个城市,两个城市之间要么有一条运输路线,要么没有。现在有一些货物需要从一个城市运往另一个城市。运输费用包含两部分:通过两个城市之间运输线路的费用,以及通过一个城市时的纳税(起点城市和目标城市除外)。要求输出费用最小,并且路径字典需序最小的线路。
这题最让人郁闷的地方就是路径的字典序,这个地方让我wrong了好几回,所谓字典序就是,如果有1-->2-->和1-->3两条路可选当输出1-->2-->3。采用的是bellman-ford算法,当判断父节点的更换的时候,就回溯查看对比路径序列,具体实现如下:
#include<iostream> #include<string.h> #include<stdio.h> #include<stack> using namespace std ; const int INF = 100000000 ; const int maxn = 1000 ; int map[maxn][maxn] ;//存放对应的图 int dist[maxn] ;//存放距离 int path[maxn] ;//存放路径 int value[maxn];//存放每个城市对应的额外花费 int source ; int end ; int n ; void bellman(int s) ; bool judge(int a , int b) ; void print(int s) ; int main() { // freopen("data.txt" , "r" , stdin) ; while(scanf("%d",&n)!=EOF&&n) { int i ; int j ; memset(map , 0 , sizeof(map)) ; memset(value , 0 , sizeof(value)) ; for(i = 0 ; i < n ; i ++) { for(j = 0 ; j < n ; j ++) { scanf("%d" , &map[i][j]) ; } } for(i = 0 ; i < n ; i ++) { scanf("%d" , &value[i]) ; } while(scanf("%d %d" , &source , &end) &&(source!=-1&&end!=-1)) { bellman(source - 1) ; printf("From %d to %d :\n", source , end) ; print(end-1) ; printf("Total cost : %d\n" , dist[end - 1]) ; printf("\n") ; } } return 0 ; } void bellman(int s) { int i ; int j ; int k ; //初始化,每个点的到源点的距离 for(i = 0 ; i < maxn ; i ++) { if(map[s][i] != -1) { dist[i] = map[s][i] ; path[i] = s ; } else { dist[i] = INF ; path[i] = -1 ; } } dist[s] = 0 ; path[s] = -1 ; for(i = 2 ; i < n ; i ++)//从dist(1)[j]递推出dist(2)[j]......dist(n-1)[j] { for(j = 0 ; j < n ; j ++)//修改每个点的dist以及path { if(j != s)//考虑其他的顶点 { for(k = 0 ; k < n ; k ++) { //顶点k到顶点j有直接路径,即计算出,经过k的路径到顶点j的最短路径 if(map[k][j] >= 0 && value[k] + dist[k] + map[k][j] < dist[j]) { dist[j] = dist[k] + value[k] + map[k][j] ; path[j] = k ; } //这里需要注意,在这里错了n多回,对于节点的父节点的更新,这里要从源点出发一次检查更新, else if(map[k][j] >= 0 && value[k] + dist[k] + map[k][j] == dist[j]) { if(judge(k , j)) path[j] = k ; } } } } } } //用栈保存临时的数据然后输出 void print(int s) { stack<int> qs ; while(s!=-1) { qs.push(s) ; s = path[s] ; } printf("Path: ") ; while(qs.size()!=1) { printf("%d-->", qs.top() + 1 ) ; qs.pop() ; } printf("%d\n" , qs.top() + 1) ; qs.pop() ; } bool judge(int a , int b) { //这里用栈来存放相应的队列 stack<int> c ; stack<int> d ; while(a!=-1) { c.push(a) ; a = path[a] ; } int x = path[b] ; while(x!=-1) { d.push(x) ; x = path[x] ; } //需要改变父节点的条件 while(!c.empty() && !d.empty()) { if(c.top() < d.top()) { return true ; } else if(c.top()==d.top()) { c.pop() ; d.pop() ; } else{ return false ; } } if(c.empty() && !d.empty()) { return b < d.top() ; } else if(!c.empty() && d.empty()) { return b > c.top() ; } return true ; }