Luogu P1073 最优贸易 (图论-SPFA)

来源:Luogu P1073,JZOJ #317

题目描述

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

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

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

假设 C C 国有 5 5 个大城市,城市的编号和道路连接情况如下图,单向箭头表示这条道路 为单向通行,双向箭头表示这条道路为双向通行。 C C 国有 n n 个大城市和 m m 条道路,每条道路连接这 n n 个城市中的某两个城市。任意两个 城市之间最多只有一条道路直接相连。这 m m 条道路中有一部分为单向通行的道路,一部分 为双向通行的道路,双向通行的道路在统计条数时也计为 1 1 条。 C C 国幅员辽阔,各地的资源分布情况各不相同,这就导致了同一种商品在不同城市的价 格不一定相同。但是,同一种商品在同一个城市的买入价和卖出价始终是相同的。 商人阿龙来到 C C 国旅游。当他得知同一种商品在不同城市的价格可能会不同这一信息 之后,便决定在旅游的同时,利用商品在不同城市中的差价赚回一点旅费。设 C C n n 个城 市的标号从 1   n 1~ n ,阿龙决定从 1 1 号城市出发,并最终在 n n 号城市结束自己的旅行。在旅游的 过程中,任何城市可以重复经过多次,但不要求经过所有 n n 个城市。阿龙通过这样的贸易方式赚取旅费:他会选择一个经过的城市买入他最喜欢的商品——水晶球,并在之后经过的另 一个城市卖出这个水晶球,用赚取的差价当做旅费。由于阿龙主要是来 C C 国旅游,他决定 这个贸易只进行最多一次,当然,在赚不到差价的情况下他就无需进行贸易。 假设 C C 国有 5 5 个大城市,城市的编号和道路连接情况如下图,单向箭头表示这条道路 为单向通行,双向箭头表示这条道路为双向通行。

解题思路

  • 这是一道 S P F A SPFA 的好题,思路是用邻接表存储图,单向边和双向边分开处理,开一个结构体数组 a [ i ] . m i n x a[i].minx 表示起点到点 i i 水晶球的最小价格, a [ i ] . a n s a[i].ans 表示起点到点 i i 最大盈利;
  • S P F A SPFA 中,用两个变量 M i n Min A n s Ans 分别存储起点到点 x x 原最小价格与当前第 x x 个球的价格中的最小值;起点到点 x x 原最大盈利与当前卖掉第 x x 个球的盈利中的最大值。再拿 M i n Min A n s Ans 分别和 a [ y ] . m i n x a[y].minx a [ y ] . a n s a[y].ans 比较,更新最优解

代码君

#include <bits/stdc++.h>
using namespace std;
int t=0,n,m;
const int MAXN=100000*2+10,MAXM=500000*2+10;  //数组范围
int vis[MAXN],q[MAXN],linkk[MAXM],p[MAXN];
struct node
{
	int y,next;
}e[MAXN];
struct ANS
{
	int minx;
	int ans;
}a[MAXM];
void insert(int x,int y)  //邻接表
{
	e[++t].y=y;
	e[t].next=linkk[x]; linkk[x]=t;
}
void SPFA(int st)
{
	memset(vis,0,sizeof(vis));
	vis[st]=1; a[1].minx=p[1]; a[1].ans=0;  //初值
	int head=1,tail=1;
	q[head]=st;  //起点进队
	for (head=1;head<=tail;head++)
	{
		int x=q[head];  //取出队头
		for (int i=linkk[x];i>0;i=e[i].next)  //邻接表查询
		{
			int y=e[i].y;  //当前边的终点
			int Min=min(a[x].minx,p[x]);  //比较x点价格是否更小
			int Ans=max(a[x].ans,p[x]-a[x].minx);  //如果在x点卖掉球是否盈利更大
			if (Min<a[y].minx || Ans>a[y].ans)
			{
				if (Min<a[y].minx)  //更新最小价格
				{
					a[y].minx=Min;
				}
				if (Ans>a[y].ans)  //更新最大盈利
				{
					a[y].ans=Ans;
				}
				if(!vis[y])  //y点还没有标记
				{
					q[++tail]=y;  //进队
					vis[y]=1;  //标记
				}
			}
		}
		vis[x]=0;  //出队
	}
}
void init()
{
	freopen("trade.in","r",stdin);
	freopen("trade.out","w",stdout);
	scanf("%d %d",&n,&m);
	for (int i=1;i<=n;i++) cin >> p[i];
	for (int i=1;i<=m;i++)
	{
		int x,y,z;
		scanf("%d %d %d",&x,&y,&z);
		if (z==1) insert(x,y);  //邻接表存储
		 else
		{
			insert(x,y);
			insert(y,x);
		}
	}
}
int main()
{
	init();
	for (int i=1;i<=100005;i++) a[i].minx=0xfffffff,a[i].ans=-0xfffffff;
	SPFA(1);
	if (a[n].ans<0) printf("0");  //如果没有答案
	 else
	printf("%d",a[n].ans);
	return 0;
}
发布了27 篇原创文章 · 获赞 33 · 访问量 1690

猜你喜欢

转载自blog.csdn.net/qq_43081996/article/details/104160410