货物运输
时间限制:
1000 ms | 内存限制:
65535 KB
难度:
4
-
描述
-
S国有n个城市,从a城到b城运货的花费有两部分组成:
(1)a城到b城的运输费
(2)途径城市的税收
例如:a 运货到 b,走路线a —> i —> j —> b ,总花费为a 到 i ,i 到 j,j 到 b 的运输费、i,j 城市的税收之和。
已知任意两个城市的运输费用,每个城市的税收,计算出,城市a到b的最小运输费。
-
输入
-
多组数据。
第一行输入整数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;
}