过路费jzyzoj 1218 (Floyd)

版权声明:本博客版权属于BIGBIGPPT,未经允许不得商用 https://blog.csdn.net/BIGBIGPPT/article/details/87925743

想直接看题解的同学可以直接点题解跳过我丢脸的过程(〃ノωノ

跟所有人一样,农夫约翰以着宁教我负天下牛,休教天下牛负我(原文:宁我负人,休教人负我)的伟大精神,日日夜夜苦思生财之道。为了发财,他设置了一系列的规章制度,使得任何一只奶牛在农场中的道路行走,都要向农夫约翰上交过路费。

农场中由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

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;
}

猜你喜欢

转载自blog.csdn.net/BIGBIGPPT/article/details/87925743
今日推荐