NYOJ-931-货物运输【floyed+路径还原】

货物运输

时间限制: 1000 ms  |  内存限制: 65535 KB
难度: 4
描述

S国有n个城市,从a城到b城运货的花费有两部分组成:

(1)a城到b城的运输费

(2)途径城市的税收

例如:运货到 b,走路线> i > j > b ,总花费为到 到 j到 的运输费、i城市的税收之和。

已知任意两个城市的运输费用,每个城市的税收,计算出,城市ab的最小运输费。

输入
多组数据。
第一行输入整数n(n不大于200,城市从1开始编号)
接下来输入n行n列的矩阵M,Mij 表示 i 城市到 j 城市的运输费,-1表示这两个城市不能直接到达。
第n+2 行 输入n个整数,第i个整数代表第i个城市的税收。
第n+3 行 输入整数m,表示有m次询问(m不大于200)。
接下来m行,每行输入两个整数u, v。
输出
对于每次询问
输出如下:
From u to v :
Path: u-->c1-->......-->ck-->v
Total cost : 

............

From e to f :
Path: e-->e1-->..........-->ek-->f
Total cost : ......


Note: 如果有多种路径方案,输出字典序最小的,每次询问后加一个换行。a城市到a城市的运费不一定为0
样例输入
4
0 5 15 -1
5 0 5 8
15 10 2 5
-1 -1 8 0
5 3 7 1
3
1 3
2 4
3 1
样例输出
From 1 to 3 :
Path: 1-->2-->3
Total cost : 13

From 2 to 4 :
Path: 2-->4
Total cost : 8

From 3 to 1 :
Path: 3-->1
Total cost : 15

 //931
/*这道题就是求最短路并且路径还原的问题。本身挺简单
但是因为字典序处理不好错误了好多次,哭瞎。
我用了一个pre[i][j],数组,表示记录j->i的第一中间点。
那么
int F=i,T=j;
do
{
	T=pre[F][T];
}while(F!=T);
递推下去就能遍历从j->i(不包括i)这些点。
pre[i][j],这个数组初始化当然为i。假如g[i][j]被更改,即i->j的路径被更改。
pre[i][j->i],就得随之改变自己的前驱。
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stack>
#define Maxsize 300
#define inf 0x3f3f3f3f
using namespace std;
stack<int> sta;
int g[Maxsize][Maxsize];
int cost[Maxsize];
int pre[Maxsize][Maxsize];
int n;
int s1[Maxsize];
int s2[Maxsize];
int intcmp(int *a,int *b)
{//判断字典序。a的字典序小的话返回1
	int len1=0;
	int len2=0;
	for(len1=0;a[len1]!=-1;len1++);
	for(len2=0;b[len2]!=-1;len2++);
	for(int i=len1-1,j=len2-1;i>=0&&j>=0;i--,j--)
	{
		if(a[i]<b[j])
			return 1;
		else if(a[i]>b[i])
			return -1;
	}
	return 0;
}
void floyed()
{
	int i,j,k;
	for(i=1;i<=n;i++)
		for(j=1;j<=n;j++)
		{
			pre[i][j]=i;
			if(g[i][j]==-1)
			{
				g[i][j]=inf;
			}
		}
	for(k=1;k<=n;k++)
		for(i=1;i<=n;i++)
			for(j=1;j<=n;j++)
			{//Floyed算法本身是个DP,所以改变判定条件就可以解决过路费的问题
				if(g[i][j]>g[i][k]+g[k][j]+cost[k])
				{
					g[i][j]=g[i][k]+g[k][j]+cost[k];
					//i->k路径不用更新
					//将k->j路径的点同步到,i->j上。
					int p,q;
					p=k;q=j;
					do
					{
						pre[i][q]=pre[p][q];
						q=pre[p][q];
					}while(q!=p);
				}
				else if(g[i][j]==g[i][k]+g[k][j]+cost[k])
				{
					int f=i,t=k;
					int loc=0;
					do
					{//遍历i->k路径,将点的序号存入s1
						s1[loc++]=t;
						t=pre[f][t];
					}while(f!=t);
					s1[loc]=-1;
					int F=i,T=j;
					loc=0;
					do
					{//遍历i->j路径,将点的序号存入s2
						s2[loc++]=T;
						T=pre[F][T];
					}while(F!=T);
					s2[loc]=-1;
					//判断字典序,是就更新路径,否则保留原来的路径
					if(intcmp(s1,s2)>0)
					{
						int p,q;
						p=k;q=j;
						do
						{
							pre[i][q]=pre[p][q];
							q=pre[p][q];
						}while(q!=p);
					}
				}
			}
	for(i=1;i<=n;i++)
		for(j=1;j<=n;j++)
			if(g[i][j]==inf)
				g[i][j]=-1;
}
void print_ans(int a,int b)
{
	printf("From %d to %d :\n",a,b);
	printf("Path: ");
	int i=a,j=b;
	while(i!=j)
	{
		sta.push(j);
		j=pre[i][j];
	}
	printf("%d", i);
	while(!sta.empty())
	{
		printf("-->%d",sta.top());
		sta.pop();
	}
	printf("\nTotal cost : %d\n",g[a][b]);
	printf("\n");
}
int main()
{
	int i,j;
	int m;
	while(scanf("%d",&n)!=EOF)
	{
		for(i=1;i<=n;i++)
			for(j=1;j<=n;j++)
				scanf("%d",&g[i][j]);
		for(i=1;i<=n;i++)
			scanf("%d",&cost[i]);
		floyed();
		scanf("%d",&m);
		int a,b;
		while(m--)
		{
			scanf("%d%d",&a,&b);
			print_ans(a,b);
		}
	}
	return 0;
}        



你可能感兴趣的:(最短路,路径还原)