【洛谷1073】【NOIP2009提高组T3】最优贸易【SPFA“双向”最短路】

Description

C国有 n个大城市和 m 条道路,每条道路连接这 n个城市中的某两个城市。任意两个城市之间最多只有一条道路直接相连。这 m 条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双向通行的道路在统计条数时也计为 1 条。

C国幅员辽阔,各地的资源分布情况各不相同,这就导致了同一种商品在不同城市的价格不一定相同。但是,同一种商品在同一个城市的买入价和卖出价始终是相同的。

商人阿龙来到 C 国旅游。当他得知同一种商品在不同城市的价格可能会不同这一信息之后,便决定在旅游的同时,利用商品在不同城市中的差价赚回一点旅费。设 C 国 n 个城市的标号从 1~ n,阿龙决定从 1 号城市出发,并最终在 n 号城市结束自己的旅行。在旅游的过程中,任何城市可以重复经过多次,但不要求经过所有 n 个城市。阿龙通过这样的贸易方式赚取旅费:他会选择一个经过的城市买入他最喜欢的商品――水晶球,并在之后经过的另一个城市卖出这个水晶球,用赚取的差价当做旅费。由于阿龙主要是来 C 国旅游,他决定这个贸易只进行最多一次,当然,在赚不到差价的情况下他就无需进行贸易。

假设 =C国有 5个大城市,城市的编号和道路连接情况如下图,单向箭头表示这条道路为单向通行,双向箭头表示这条道路为双向通行。

在这里插入图片描述

假设 1~n号城市的水晶球价格分别为 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 条道路的信息(每条道路所连接的两个城市的编号以及该条道路的通行情况)。请你告诉阿龙,他最多能赚取多少旅费。

Imput

第一行包含 2 个正整数 n和 m,中间用一个空格隔开,分别表示城市的数目和道路的数目。

第二行 n 个正整数,每两个整数之间用一个空格隔开,按标号顺序分别表示这 n 个城市的商品价格。

接下来 m 行,每行有 3个正整数x,y,z,每两个整数之间用一个空格隔开。如果 z=1,表示这条道路是城市 x到城市 y之间的单向道路;如果 z=2,表示这条道路为城市 x 和城市y之间的双向道路。

Output

一 个整数,表示最多能赚取的旅费。如果没有进行贸易,则输出 0。

输入样例

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

输出样例

5

分析

非常冗长的题目描述让人看起来非常厌烦,现在来说说题目本质:在走过的路中,从起点到终点的这条路径上的最小的买入价值点和最大的卖出价值点。
思路:两遍SPFA(并列,不是嵌套)O(n^2)即可

现在我们并不知道最小的买入价值点和最大的卖出价值点在哪,以及能否从起点到达买入点,能否从买入点到达卖出点,能否从卖出点到达终点。
这里就可以引入DP的思想。从起点开始遍历,点 i 的最小的买入价值就是前 i 个点的最小的买入价值;同样的,从终点开始往回找,点 j 的最大卖出价值就是 j 到终点之间那么多点的最大卖出价值。【这里是重点】
在找到每个点的最小的买入价值和最大的卖出价值的时候,再将每个点都遍历一遍求出最终的最大利润

上代码

#include<bits/stdc++.h>
#include<cstring>
using namespace std;
int m,n,k,a[100100],maxx[1000010],minn[1000010],v[1010001],f[1000010],ans;
struct node
{
	int x,y,next,w;
} xx[1000010];
node yy[1000010];
int tot,tot1,hd1[1000010],hd[1000010];
void add1(int x,int y,int w)
{
	tot++;
	xx[tot].x=x;
	xx[tot].y=y;
	xx[tot].w=w;
	xx[tot].next=hd[x];
	hd[x]=tot;
}
void add2(int x,int y,int w)
{
	tot1++;
	yy[tot1].x=x;
	yy[tot1].y=y;
	yy[tot1].w=w;
	yy[tot1].next=hd1[x];
	hd1[x]=tot1;
}
void spfa(int x)
{
	memset(minn,0x3f,sizeof(minn));
	minn[x]=a[x];
	v[x]=1;
	f[1]=x;
	int h=0,t=1;
	while(h<t)
	{
		h=h%100010+1;
		int x1=f[h];
		for(int i=hd[x1];i;i=xx[i].next)
		{
			if(minn[xx[i].y]>min(a[xx[i].y],minn[x1]))
			{
				minn[xx[i].y]=min(a[xx[i].y],minn[x1]);
				if(v[xx[i].y]==0)
				{
					t++;
					v[xx[i].y]=1;
					f[t]=xx[i].y;
				} 
			}
		}
		v[x1]=0;
	}
} 
void spfa1(int x)
{
	memset(v,0,sizeof(v));
	maxx[x]=a[x];
	v[x]=1;
	f[1]=x;
	int h=0,t=1;
	while(h<t)
	{
		h=h%100010+1;
		int x1=f[h];
		for(int i=hd1[x1];i;i=yy[i].next)
		{
			if(maxx[yy[i].y]<max(a[yy[i].y],maxx[x1]))
			{
				maxx[yy[i].y]=max(a[yy[i].y],maxx[x1]);
				if(v[yy[i].y]==0)
				{
					t++;
					v[yy[i].y]=1;
					f[t]=yy[i].y;
				}
			}
		}
		v[x1]=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)
		{
			add1(x,y,a[y]);
			add2(y,x,a[x]);
		}
		else
		{
			add1(x,y,a[y]);
			add1(y,x,a[x]);
			add2(x,y,a[y]);
			add2(y,x,a[x]); 
		} //邻接表(双向) 
	}
	spfa(1);//最大值spfa
	spfa1(n);//最小值spfa 
	ans=0;
	for(int i=1;i<=n-1;i++)
	{
		ans=max(ans,maxx[i]-minn[i]);
	}//定点x枚举
	cout<<ans; 
	return 0;
} 

剧终

发布了63 篇原创文章 · 获赞 61 · 访问量 5481

猜你喜欢

转载自blog.csdn.net/dglyr/article/details/104023963
今日推荐