2020寒假【gmoj1595】【GDKOI訓練】【過路費toll】【floyed最短路(變式)】

題目描述

跟所有人一樣,農夫約翰以着寧教我負天下牛,休叫天下牛負我的偉大精神,日日夜夜苦思生財之道。爲了發財,他設置了一系列的規章制度,使得任何一隻奶牛在農場中的道路行走,都要向農夫約翰上交過路費。

農場中由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到草地3的道路的“邊過路費”爲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。

輸入

第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

輸出

第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

樣例輸出

8
9

分析

這題就是求兩點之間最短路,特別的,最短路定義爲邊權和加最大點權。(n≤250)

題解:多源最短路用floyed。先把點權排序,從小到大枚舉在中轉點點權比兩端點點權大的時候,說明中轉點是路徑上權值最大的點,這時可以更新答案。
對於每次Floyd找到的中間點k,點權的最大值一定在i、j、k三者中。因爲k是從小到大枚舉的,所以目前路徑中除了起點i和終點j外,k是點權最大的點了。

上代碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int f[251][251],dis[251][251],g[251],s[251];
int n,m,p,q,t;
inline bool cmp(int x,int y)
{
	return g[x]<g[y];	
}
int main()
{
	freopen("toll.in","r",stdin);
	freopen("toll.out","w",stdout);
	cin>>n>>m>>p; 
	memset(f,1,sizeof(f));
	memset(dis,1,sizeof(dis));
	for(int i=1;i<=n;i++) 
	{
		s[i]=i;
		cin>>g[i];
		f[i][i]=g[i];		
	}	
	sort(s,s+n+1,cmp);
	for(int i=1;i<=m;i++)
	{
		int a,b,c;
		cin>>a>>b>>c;
		dis[a][b]=dis[b][a]=min(dis[a][b],c);
	}	
	for(int k=1;k<=n;k++)
	{
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				dis[s[i]][s[j]]=min(dis[s[i]][s[j]],dis[s[i]][s[k]]+dis[s[k]][s[j]]);	
				f[s[i]][s[j]]=min(f[s[i]][s[j]],dis[s[i]][s[j]]+max(g[s[i]],max(g[s[k]],g[s[j]])));	
				/*算上i,j,k中最大點權*/						
			}
		}	
	}	
	for(int i=1;i<=p;i++)
	{
		cin>>q>>t;
		if(f[q][t]>=16843010) cout<<-1<<endl;
		else cout<<f[q][t]<<endl; 
	}
	fclose(stdin);
	fclose(stdout);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章