hdu1385 floyd+保存路径。另附spfa

怎么保存路径?

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值