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 ;
}