题目描述
约翰家有 N 片草地,编号为 1 到 N ,彼此之间由 M 条双向道路连接,第 i 条道路连接了 Ai 和 Bi,两片草地之间可能有多条道路,但没有道路会连接同一片草地,现有的道路可以保证任意两片草 地都是连通的。
有一天,约翰宣布奶牛走路要收过路费,只要奶牛走过第 i 条道路,就要收费 Li 元。此外,约 翰还要求每头奶牛购买牌照,他为每片草地设置了牌照标准,如果奶牛购买的牌照价格低于某片草地 的标准,她将被禁止进入那片草地。第 i 片草地的牌照标准为 Ci。
新政策一出,奶牛们敢怒不敢言,有 Q 头奶牛向你咨询最省钱的走路办法,第 i 头奶牛要从草地 Si 走到 Ti。请你帮她们算算,选择什么样的路线才能最省钱?
输入格式
第一行:三个整数 N ,M 和 Q,1 ≤ N ≤ 250; 1 ≤ M ≤ 10000; 1 ≤ Q ≤ 10000
• 第二行到第 N + 1 行:第 i + 1 行有一个整数 Ci,1 ≤ Ci ≤ 10^6
• 第 N + 2 行到第 N + M + 1 行:第 i + 1 行有三个整数 Ai,Bi 和 Li,1 ≤ Ai ; B i ≤ N ,1 ≤ Li ≤ 10^6
• 第 N + M +2 行到第 N + M + Q +1 行:第 i + N + M +1 行有两个整数 Si 和 Ti,1 ≤ Si,Ti ≤ N
输出格式
第一行到第 Q 行:第 i 行有一个整数,表示从 Si 到 Ti 的最低费用是多少
样例输入
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
解释 最 好 办 法 分 别 是 1 → 3 → 5 → 4 和 2 → 5 → 3
题解
道路的最小费用Floyd很好求,我本来想分别求出i到j的道路最小费用和最小牌照标准,但发现道路费用最小时加上这条道路上的最小牌照标准不一定是最优的。
一条路上的牌照标准是这条路上所有牌照标准的最大值,如果在更新道路最小费用时可以保证牌照标准是递增的,问题就解决了。
于是可以用排序。
记录节点原来的位置和牌照标准Ci,把Ci从小到大排,就可以保证更新道路最小费用时牌照标准是递增的。
因为Ci是递增的,枚举i到j的中间点k时,i到j的路上牌照标准最大值一定在i,j,k这三个节点上。为什么呢?
首先,i和j不在Ci递增的行列中,所以不能保证牌照标准最大值不在i或j上;接着,由于k在Ci递增的行列中,k前的节点牌照标准值一定比k小,而k后的节点还没有经过,是后面要用来更新k的。
由于排序后节点的顺序打乱了,数组第i个位置的C不再是第i个节点的C值,而Floyd是按节点原来的顺序求最短路的,要找原来第i个节点的C值就比较麻烦。我们可以再开个数组w,记录原来第i个节点的C值,就可以方便的查找了。
#include <algorithm> #include <cstdio> struct node{ int id,v; }; node c[305]; int f[305][305],a[305][305],w[305]; bool cmp(node x,node y) { return x.v<y.v; } int min(int x,int y) { return x<=y?x:y; } int max(int x,int y) { return x>=y?x:y; } int main() { int n,m,Q,x,y,z,i,j,k,s,t; scanf("%d%d%d",&n,&m,&Q); for (i=1;i<305;i++) for (j=1;j<305;j++) a[i][j]=f[i][j]=1e9; for (i=1;i<=n;i++) scanf("%d",&c[i].v), f[i][i]=c[i].v, c[i].id=i; for (i=1;i<=m;i++) { scanf("%d%d%d",&x,&y,&z); if (z<a[x][y]) a[x][y]=a[y][x]=z; } std::sort(c+1,c+n+1,cmp); for (i=1;i<=n;i++) w[c[i].id]=c[i].v; for (int ii=1;ii<=n;ii++) { k=c[ii].id; for (i=1;i<=n;i++) for (j=1;j<=n;j++) a[i][j]=min(a[i][j],a[i][k]+a[j][k]), f[i][j]=min(f[i][j],max(w[i],max(w[j],c[ii].v))+a[i][j]); } for (i=1;i<=Q;i++) scanf("%d%d",&s,&t), printf("%d ",f[s][t]); return 0; }