跟所有人一样,农夫约翰以着宁教我负天下牛,休教天下牛负我(原文:宁我负人,休教人负我)的伟大精神,日日夜夜苦思生财之道。为了发财,他设置了一系列的规章制度,使得任何一只奶牛在农场中的道路行走,都要向农夫约翰上交过路费。
农场中由N(1 <= N <= 250)片草地(标号为1到N),并且有M(1 <= M <= 10000)条双向道路连接草地A_j和B_j(1 <= A_j <= N; 1 <= B_j <= N)。奶牛们从任意一片草地出发可以抵达任意一片的草地。FJ已经在连接A_j和B_j的双向道路上设置一个过路费L_j(1 <= L_j <= 100,000)。
可能有多条道路连接相同的两片草地,但是不存在一条道路连接一片草地和这片草地本身。最值得庆幸的是,奶牛从任意一篇草地出发,经过一系列的路径,总是可以抵达其它的任意一片草地。
除了贪得无厌,宁智贤都不知道该说什么好。FJ竟然在每片草地上面也设置了一个过路费C_i(1 <= C_i <= 100000)。从一片草地到另外一片草地的费用,是经过的所有道路的过路费之和,加上经过的所有的草地(包括起点和终点)的过路费的最大值。
任劳任怨的牛们希望去调查一下她们应该选择那一条路径。她们要你写一个程序,接受K(1 <= K <= 10,000)个问题并且输出每个询问对应的最小花费。第i个问题包含两个数字s_i和t_i(1 <= s_i <= N; 1 <= t_i <= N; s_i != t_i),表示起点和终点的草地。
考虑下面这个包含5片草地的样例图像:
从草地1到草地2的道路的“边过路费”为3,草地2的“点过路费”为5。
要从草地1走到草地4,可以从草地1走到草地3再走到草地5最后抵达草地4。如果这么走的话,
需要的“边过路费”为2+1+1=4,需要的点过路费为4(草地5的点过路费最大),所以总的花
费为4+4=8。
而从草地2到草地3的最佳路径是从草地2出发,抵达草地5,最后到达草地3。这么走的话,边
过路费为3+1=4,点过路费为5,总花费为4+5=9。
输入格式 Input Format
* 第1行: 三个空格隔开的整数: N, M和K
- 第2到第N+1行: 第i+1行包含一个单独的整数: C_i
- 第N+2到第N+M+1行: 第j+N+1行包含3个由空格隔开的整数: A_j, B_j和L_j
- 第N+M+2倒第N+M+K+1行: 第i+N+M+1行表示第i个问题,包含两个由空格隔开的整数s_i和t_i
输出格式 Output Format- 第1到第K行: 第i行包含一个单独的整数,表示从s_i到t_i的最小花费。
样例输入 Sample Input
- 第1到第K行: 第i行包含一个单独的整数,表示从s_i到t_i的最小花费。
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
样例输出 Sample Output
8
9
来源:usco dec 09
丢脸qwq
emm这道题乍一看我以为是点权加边权的最短路问题,于是我当时就兴致冲冲地打了一遍spfa,加边的时候把每条边的边权都加上了它所指向点的点权。(看不懂的话就是边x到y加上了y的点权)然后踌躇满志地跑了样例发现没有过。(⊙_⊙)!!!
然后我才又去看了一下题(从下往上)发现,诶——,不对,它点权加的方式和我想的有点不一样。Σ(⊙▽⊙"a
于是我仔细地看了一遍一遍解释样例图的那个内容,发现它只加了一个点权,当时我还在心里模拟了半天,发现怎搞都不对Σ(☉▽☉"aΣ(☉▽☉"aΣ(☉▽☉"a
最后,我终于在上面找到了一句话。。
_φ( °-°)/ 。。。。
原来如此。
这道题的每条路径只需要加上路过点中点权最大的那个啊。
0.0.0.0.0.0.0.0.0.0
CCF提醒您:
做题千万条,看题第一条。
看题不规范,代码两行泪。
或许我的spfa还有救于是我默默地删了我之前的代码。重新开始分析题
题解
其实呢这道题我们先注意到的不应该是因为有点权的参与,导致选择路径时情况更加的复杂。
我们首先注意到的应该是多次询问还有那不是很大的数据范围,显然因为多次询问所以我们用Dijkstra或是spfa或是Bellman Ford(好吧根本不会想到它),这种求单源最短(一个点到其他所有点的最短路)的算法是不优的,因为每次询问你都要跑一次,很容易超时好吧。
于是我们就能想到用邻接矩阵的Floyd,它表示的是两点间的最短距离,对于这道题来说就是量身定制,跑一边Floyd,一劳永逸啊。
于是我们就用Floyd做这道题。
那我们就思考怎么才能让点权也参与答案更新的其中。
那么我们可以在每次找到i到j的最短距离后再开一个数组记录加上其中一个点的总费用,那么在更新时k,i,j是一定在路径上的,我们每次选择三个点权最大的值让i,j的距离加上它,更新时每次取最小。
假设用d[N][N]记录答案
那么在每次循环时判断如果f[i][j]被更新过了
那么d[i][j] = min(d[i][j], f[i][j] + max( i的点权 , max( y的点权 , k的点权 ) ) );
类似于动态规划的一个过程,这样我们得到的d[i][j]就是i到j的最小费用。
每次询问时只要输出d[i][j]就好了。
代码
#include<bits/stdc++.h>
#define D 300
#define MAXN 100010
#define INF 0x3f3f3f3f
using namespace std;
struct node{
int w,id;
}pot[MAXN];
int h[MAXN];
int f[D][D],d[D][D];
int n,m,k;
inline bool memcy(node a,node b){
return a.w < b.w;
}
void floyd(){
for(int k = 1; k <= n; ++k){
for(int i = 1; i <= n; ++i){
for(int j = 1; j <= n; ++j){
if(i == j) continue;
f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
if(f[i][j] == f[i][k] + f[k][j])
d[i][j] = min(d[i][j],f[i][j] + max(pot[k].w, max(pot[i].w, pot[j].w)));
}
}
}
}
int main()
{
memset(f,INF,sizeof(f));
// memset(d,INF,sizeof(d));
scanf("%d%d%d",&n,&m,&k);
for(int i = 1; i <= n; ++i){
scanf("%d",&pot[i].w);
pot[i].id = i;
}
sort(pot + 1 , pot + n + 1, memcy);
for(int i = 1; i <= n; ++i){
h[pot[i].id] = i;
}
for(int i = 1; i <= m; ++i){
int x,y,z; scanf("%d%d%d",&x,&y,&z);
f[h[x]][h[y]] = f[h[y]][h[x]] = min(f[h[x]][h[y]], z);
}
for(int i = 1; i <= n; ++i)
f[i][i] = 0;
memset(d,INF,sizeof(d));
floyd();
for(int i = 1; i <= k; ++i){
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",d[h[x]][h[y]]);
}
return 0;
}