【洛谷P1073】最優貿易【spfa+鏈表+反圖】

題目描述

C國有 n個大城市和 m 條道路,每條道路連接這 n個城市中的某兩個城市。任意兩個城市之間最多隻有一條道路直接相連。這 m 條道路中有一部分爲單向通行的道路,一部分爲雙向通行的道路,雙向通行的道路在統計條數時也計爲1條。

C國幅員遼闊,各地的資源分佈情況各不相同,這就導致了同一種商品在不同城市的價格不一定相同。但是,同一種商品在同一個城市的買入價和賣出價始終是相同的。

商人阿龍來到 C國旅遊。當他得知同一種商品在不同城市的價格可能會不同這一信息之後,便決定在旅遊的同時,利用商品在不同城市中的差價賺回一點旅費。設 C國 n 個城市的標號從 1~ 1n,阿龍決定從1號城市出發,並最終在 n號城市結束自己的旅行。在旅遊的過程中,任何城市可以重複經過多次,但不要求經過所有 n個城市。阿龍通過這樣的貿易方式賺取旅費:他會選擇一個經過的城市買入他最喜歡的商品――水晶球,並在之後經過的另一個城市賣出這個水晶球,用賺取的差價當做旅費。由於阿龍主要是來 C國旅遊,他決定這個貿易只進行最多一次,當然,在賺不到差價的情況下他就無需進行貿易。

假設C國有 5個大城市,城市的編號和道路連接情況如下圖,單向箭頭表示這條道路爲單向通行,雙向箭頭表示這條道路爲雙向通行。

在這裏插入圖片描述

假設 1~1n 號城市的水晶球價格分別爲 4,3,5,6,1。
阿龍可以選擇如下一條線路:1->2->3->5,並在2號城市以3的價格買入水晶球,在3號城市以5的價格賣出水晶球,賺取的旅費數爲2。

阿龍也可以選擇如下一條線路 1->4->5->4->5,並在第1次到達5號城市時以1的價格買入水晶球,在第 2 次到達 4 號城市時以 6 的價格賣出水晶球,賺取的旅費數爲 5。

現在給出 n個城市的水晶球價格,m條道路的信息(每條道路所連接的兩個城市的編號以及該條道路的通行情況)。請你告訴阿龍,他最多能賺取多少旅費。

輸入格式

第一行包含 2 個正整數 n 和 m,中間用一個空格隔開,分別表示城市的數目和道路的數目。

第二行 n 個正整數,每兩個整數之間用一個空格隔開,按標號順序分別表示這 n 個城市的商品價格。

接下來 m 行,每行有 3個正整數x,y,z每兩個整數之間用一個空格隔開。如果 z=1,表示這條道路是城市 x到城市 y之間的單向道路;如果 z=2,表示這條道路爲城市 x和城市 y之間的雙向道路。

輸出格式

一 個整數,表示最多能賺取的旅費。如果沒有進行貿易,則輸出0。

輸入輸出樣例

輸入 #1

5 5 
4 3 5 6 1 
1 2 1 
1 4 1 
2 3 2 
3 5 1 
4 5 2 

輸出 #1

5

分析&說明:

這道題是要用SPFA做才合適,而且通過學校一位大佬的講解,

才知道:

這道題要用兩張圖:一張原圖一張反圖。而且要跑兩遍SPFA,分別算出貿易的最小值最大值,在最後求出max(最大值與最小值的差),就是最優貿易

CODE:

這可能是我打過最長的代碼了。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,k,a[100100],maxn[1000100]/*最大值數組*/,minn[1000100]/*最小值數組*/,v[1010001],f[1000100],ans;
struct node{
	int x,y,next,w;
}c[1000010],b[1000010];
int tot,tot2,head1[1001000],head2[1001000];//這些變量都有兩個,兩張圖,兩遍spfa
void add(int x,int y,int w)
{
	tot++;
	b[tot].x=x;
	b[tot].y=y;
	b[tot].w=w;
	b[tot].next=head1[x];
	head1[x]=tot;
	//第一張鄰接表
}
void add2(int x,int y,int w)
{
	tot2++;
	c[tot2].x=x;
	c[tot2].y=y;
	c[tot2].w=w;
	c[tot2].next=head2[x];
	head2[x]=tot2;
	//第二張鄰接表
}
void spfa(int x)
{
    //最小值spfa
	memset(minn,0x7f,sizeof(minn));  //初值
	minn[x]=a[x];
	v[x]=1;
	f[1]=x;
	int head=0,tail=1;
	while(head<tail)  //類似廣搜實現
	{
		head=head%100010+1;
		int x2=f[head];
		for(int i=head1[x2];i;i=b[i].next)  //鄰接表
		{
			if(minn[b[i].y]>min(a[b[i].y],minn[x2]))
			{
				minn[b[i].y]=min(a[b[i].y],minn[x2]);  //求最小
				if(!v[b[i].y])
				{
					tail++;  //入隊
					v[b[i].y]=1;
					f[tail]=b[i].y;
				}
			}
		}
		v[x2]=0; 
	}
}
void spfa2(int x)
{
	//最大值spfa
	int head=0,tail=1;
	memset(v,0,sizeof(v));  //初值
	maxn[x]=a[x];
	v[x]=1;
	f[1]=x;
	while(head<tail)
	{
		head=head%100010+1;
		int x2=f[head];
		for(int i=head2[x2];i;i=c[i].next)
		{    //鄰接表
			if(maxn[c[i].y]<max(a[c[i].y],maxn[x2]))
			{
				maxn[c[i].y]=max(a[c[i].y],maxn[x2]);  //求最大
				if(v[c[i].y]==0)
				{
					tail++;  //繼續入隊
					v[c[i].y]=1;
					f[tail]=c[i].y;
				}
			}
		}
		v[x2]=0;
	}
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	for(int i=1;i<=m;i++)
	{
		int x,y,k;
		cin>>x>>y>>k;
		if(k==1)
		{
			add(x,y,a[y]);
			add2(y,x,a[x]);
		}
		else
		{
			add(x,y,a[y]);
			add(y,x,a[x]);  //原圖
			add2(x,y,a[y]);
			add2(y,x,a[x]);  //反圖
		}
	}
	spfa(1);spfa2(n);ans=0;  //跑最大值與最小值spfa
	for(int i=2;i<n;i++)
	ans=max(ans,maxn[i]-minn[i]); //最大差
	cout<<ans;
	
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章