怎么保存路径?
在spfa&Dijkstra&Bellman-Ford在更新点的时候,只要path[i]=v记录下前驱即可。
而在floyd里面,需要设二维数组,定义path[a][b]:=起点a到达起点b经过path[a][b]到达b,当father[a][b]=b时,结束路径。
void floyd()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
f[i][j]=j; //初始化每一段路径ab,path[a][b]=b;意味点a只要通过path[a][b]==b即可到达b
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
int temp=graph[i][k]+graph[k][j]+tax[k];
if(temp<graph[i][j])
{
graph[i][j]=temp;//每次以k为跳板更新时,应设path[i][j]=k
f[i][j]=f[i][k];//但是路径i->k或许并非直接从i到达k,或许还通过其他跳板,所以应让path[i][j]=path[i][k].
}
else if(temp==graph[i][j]&&f[i][j]>f[i][k])
f[i][j]=f[i][k]; //字典序,换成小的。
}
}
为什么令path[i][j]=path[i][k],而并非令path[i][j]=k:
实际上dist[i][j]=dist[i][k]+dist[k][j],意味着i->..->k->...->j (i->j通过点k)
由于i->.....->k还还存在其路径,即path[i][k]保存的路径
所以,为了保存i->k的路径,须令path[i][j]=path[i][k]
如下,dist[1][5]=dist[1][4]+dist[4][5],但是并非1->4->5,而在1->4还有其他途径,即1->2->4->5
所以保存路径为path[1][5]=path[1][4].....(此时path[1][4]==2)
floyd:
#include <cstdio>
#include<string.h>
#include <iostream>
#include <algorithm>
#include<functional>
using namespace std;
typedef long long ll;
int n,graph[1000][1000];
int tax[1000];
int f[1000][1000];
inline void read(int &m)
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
m=x*f;
}
void floyd()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
f[i][j]=j; //初始化每一段路径ab,path[a][b]=b;
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
int temp=graph[i][k]+graph[k][j]+tax[k];
if(temp<graph[i][j])
{
graph[i][j]=temp;
f[i][j]=f[i][k];//路径ij,从k经过比较近,则更新path[i][j]=path[i][k]..
}
else if(temp==graph[i][j]&&f[i][j]>f[i][k])
f[i][j]=f[i][k]; //字典序,换成小的。有点难理解。
}
}
int main()
{
while(read(n),n)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
read(graph[i][j]);
if(graph[i][j]==-1||i==j) //i==j时也令其无限大。看代码尾部介绍。
graph[i][j]=11111111;
}
for(int i=1;i<=n;i++)
read(tax[i]);
floyd();
int a,b;
while(read(a),read(b),a!=-1&&b!=-1)
{
printf("From %d to %d :\n",a,b);
printf("Path: %d",a);
int temp=a;
while(temp!=b) //输出路径
{
printf("-->%d",f[temp][b]);
temp=f[temp][b];
}
printf("\nTotal cost : %d\n\n",a!=b?graph[a][b]:0);
}
}
return 0;
}
/*为什么令i==j时即graph[i][i]==无限大?
6
0 100 100 100 1 1
100 0 100 1 100 100
100 100 0 1 100 100
100 100 100 100 100 100
100 1 100 100 0 100
100 100 1 100 100 0
0 0 0 0 0 0
1 4
路径保存的一点小问题
数据使 dist[1][4]==dist[1][1]+dist[1][4]+tax[1] 成立
由于path[1][1]==1 < path[1][4]==4,更新path[1][4]=1
所以路径1->4在点1处无限徘徊。
输出变成1->1.....1..1..1.1.1.1.1.1.1.1.1.
当设dist[i][i]=无限大时,可解决这个问题
网上好多代码并没有注意到这个问题,当然这只是路径问题,并不影响1->4的权值
*/
spfa
#include <cstdio>
#include<string.h>
#include <iostream>
#include <algorithm>
#include<functional>
using namespace std;
typedef long long ll;
int n,graph[1000][1000];
int tax[1000];
int f[1000],dist[1000];
inline void read(int &m)
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
m=x*f;
}
void fout(int k)
{
if(f[k]==-1)return ;
fout(f[k]);
printf("-->%d",k);
}
void spfa(int k)
{
bool vis[1000];
memset(vis,0,sizeof(vis));
fill(dist,dist+1000,1111111);
for(int i=1;i<=n;i++)f[i]=n+1;
f[k]=-1;dist[k]=0;vis[k]=1;
queue<int>que;
que.push(k);
while(!que.empty())
{
int v=que.top();
que.pop();
vis[v]=0;
for(int i=1;i<=n;i++)
{
if(dist[i]>dist[v]+graph[v][i]+tax[i])
{
if(i==v)continue;
dist[i]=dist[v]+graph[v][i]+tax[i];
f[i]=v;
if(!vis[i])
vis[i]=1,que.push(i);
}
else if(dist[i]==dist[v]+graph[v][i]+tax[i]) //判断字典序。需不需要换。
{
int temp=i;
int num1[1000],num2[1000],id1=0,id2=0;
while(f[temp]!=-1)
num1[id1++]=temp,temp=f[temp];
temp=v;
num2[id2++]=i;
while(f[temp]!=-1)
num2[id2++]=temp,temp=f[temp];
id1--,id2--;
bool ok=0;//0代表不换,1代表换
while(id1>=0&&id2>=0)
{
if(num1[id1]<num2[id2])
break;
else if(num1[id1]>num2[id2])
{
ok=1;
break;
}
id1--,id2--;
}
if(ok||(id2<0&&id1>=0))
f[i]=v;
}
}
}
}
int main()
{
while(read(n),n)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
read(graph[i][j]);
if(graph[i][j]==-1)
graph[i][j]=11111111;
}
for(int i=1;i<=n;i++)
read(tax[i]);
int a,b,temp=0;
while(read(a),read(b),a!=-1&&b!=-1)
{
swap(temp,tax[b]);
spfa(a);
swap(temp,tax[b]);
printf("From %d to %d :\n",a,b);
printf("Path: %d",a);
fout(b);
printf("\nTotal cost : %d\n\n",dist[b]);
}
}
return 0;
}