城市交通费
【问题描述】
有 n 个城市, 编号 1~n。 其中 i 号城市的繁华度为 pi。 省内有 m 条可以双向同行的高速
公路, 编号 1~m。 编号为 j 的高速公路连接编号为 aj 和 bj 两个城市, 经过高速公路的费用
是 wj。 若从城市 x 出发到某城市 y, 除了需要缴纳高速公路费用, 还要缴纳“城市建设费”
(为从 x 城市到 y 城市所经过的所有城市中繁华度的最大值, 包括 x 和 y 在内) 。
现提出 q 个询问, 每个询问给出一组 x 和 y, 你需要回答从 x 出发到 y 城市, 所需要的
最低交通费(高速公路费+城市建设费) 是多少。
【输入】
第一行三个整数 n,m,q。
第二行 n 个整数, 表示 p1~pn。
接下来 m 行中, 每行 3 个正整数, 第 j 行包含 Aj, Bj, Wj。
随后 Q 行每组两个正整数 x, y 表示一组询问。
【输出】
共 Q 行, 为对 Q 个问题的回答: x 城市到 y 城市的最小交通费用。
【样例输入】
5 7 2
2 5 3 3 4
1 2 3
1 3 2
2 5 3
5 3 1
5 4 1
2 4 3
3 4 4
1 4
2 3
【样例输出】
8 9
【数据范围及约定】
n≤250, m≤20000, Q≤10000, Pi≤10000, Wj≤2000, 保证任意两个城市可以互相到达。
【样例说明】
图中, 代表城市的格子中第一个数字是城市编号, 第二个红色数字是该城市的繁华度。
(1) 从城市 1 到城市 4 的最小交通费用路线是: 1 3 5 4; 公路费是 2+1+1=4; 城市建设费是
max{2,3,4,3}=4; 总交通费用 4+4=8。
(2) 从城市 2 到城市 3 的最小交通费用路线是: 2 5 3; 公路费是 3+1=4; 城市建设费是
max{5,4,3}=5; 总交通费用 4+5=9。
由于这个题是多组询问,显然要用floyd做,但关键是如何处理繁华度这个东西,因为最短路径和最小繁华度的路径很有可能不是一条路
我们可以考虑对繁华度进行排序,并记录下来排序之后每个点对应的编号,然后再跑一遍floyd
但是这样的正确性是如何保证的?这就要说到floyd的本质思想了
因为floyd的本质其实是DP,我们记Fk,i,j表示当前情况下从i到j在经过中间点的标号不超过k时的最短路(1~k这些点可以经过也可以不经过),那么DP的状态转移方程就是Fk,i,j=min(Fk-1,i,j,Fk-1,i,k+Fk-1,k,j)也就是说在经过k和不经过k之中取最大值
那么对于这道题来说,Fk,i,j 就表示在经过繁华度前k小的城市的前提下,从i~j的最短路径,那么因为最终的答案要考虑最大繁华度,又由于繁华度是按照从小到大的顺序排序的,所以最大繁华度只能在i,j,k 之间选取。
for(int k=1;k<=n;k++)//floyd枚举中间点 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { a[i][j]=min(a[i][j],a[i][t[k]]+a[t[k]][j]);//松弛两个点之间的最短路 f[i][j]=min(f[i][j],a[i][j]+max(p[i],max(p[j],p[t[k]])));//最小花费
}
那么有的小伙伴(@YY)就要问了,如果我们刚开始没有用k来更新最短路,那么下面岂不是还会用k来更新,这不就矛盾了吗?
其实并不矛盾
当点k不经过的时候,a[i][j]是没有被更新的,而f[i][j]在取min之前仍然是在经过繁华度前k-1小的城市的前提下,从i~j的最短路径。
以下分两种情况
1.p[k]<max(p[i],p[j])
此时显然p[k]不会对答案产生影响
2.p[k]>max(p[i],p[j])
此时因为这些城市已经按照繁华度排序了,所以有p[k]>p[k-1],也就是说由于取min,p[k]不会对答案产生影响
所以这种担心是不成立的
代码:
#include<iostream> #include<cstring> #include<cstdio> #include<queue> #include<algorithm> using namespace std; int n,m,q,p[300],aj,bj,wj,x,y,f[300][300],a[300][300],top,t[300]; int cmp(int x,int y) { return p[x]<p[y]; } int main() { //freopen("road.in","r",stdin); //freopen("road.out","w",stdout); memset(a,63,sizeof(a)); top=0; scanf("%d%d%d",&n,&m,&q); for(int i=1;i<=n;i++) scanf("%d",&p[i]); for(int i=1;i<=m;i++) { scanf("%d%d%d",&aj,&bj,&wj); a[aj][bj]=min(a[aj][bj],wj); a[bj][aj]=min(a[bj][aj],wj); } //邻接矩阵存图,由于毒瘤数据有重复的边,所以取最小值 for(int i=1;i<=n;i++) { a[i][i]=0;//自己到自己的距离为0 t[i]=i;//节点的标号 } sort(t+1,t+1+n,cmp);//将城市按照繁华度排序,相当于是开了一个结构体 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) f[i][j]=a[i][j]+max(p[i],p[j]); //初始化 for(int k=1;k<=n;k++)//floyd枚举中间点 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { a[i][j]=min(a[i][j],a[i][t[k]]+a[t[k]][j]);//松弛两个点之间的最短路 f[i][j]=min(f[i][j],a[i][j]+max(p[i],max(p[j],p[t[k]])));//最小花费 } for(int i=1;i<=q;i++) { scanf("%d%d",&x,&y); printf("%d ",f[x][y]); } //fclose(stdin); //fclose(stdout); return 0; }