最优贸易题解(双向bfs+spfa)(洛谷P1073)(noip2009提高组第三题)

最优贸易 NOIP2009洛谷P1073(双向bfs+spfa)

日常膜拜大佬:财神万岁!!!!!!!!!!!!!!!!!!!!!!!!!!
一遍AC的这道题,,有点小骄傲~~
原题链接:https://www.luogu.org/problem/P1073

题目:

题目描述

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 条道路的信息(每条道路所连接的两个城市的编号以及该条道路的通行情况)。请你告诉阿龙,他最多能赚取多少旅费。

输入格式

第一行包含 2 个正整数 n和 m,中间用一个空格隔开,分别表示城市的数目和道路的数目。
第二行 n 个正整数,每两个整数之间用一个空格隔开,按标号顺序分别表示这 n 个城市的商品价格。
接下来 m 行,每行有 3 个正整数x,y,z,每两个整数之间用一个空格隔开。如果 z=1,表示这条道路是城市 x到城市 y之间的单向道路;如果 z=2,表示这条道路为城市 x 和城市y 之间的双向道路。

输出格式

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

样例:
输入: 输出:5
5 5
4 3 5 6 1
1 2 1
1 4 1
2 3 2
3 5 1
4 5 2

题解

1.双向bfs(如果你连bfs是什么都不知道就不要看图论的题了。。。)

看题,题中说白了就是要求从1出发,最后到n,期间怎么走无所谓,要求差价最大。
首先可以很明白的看出,如果一个城市不能由1到达或者不能到达n那么这个城市是没用的。所以双向存图然后跑两遍bfs,如果两遍bfs都能到访问到某一个点那么这个点才有用。(第一遍bfs正向存图,然后从1号点开始跑,能到的点都是1号点能够到达的点,
第二遍bfs反向存图,从n号点开始跑,能到达的点都的能够到达n号点的点(是不是有点绕。。)
现在来解释清楚你的疑惑~~(dalao自动忽略)~~
Q:什么是反向存图?为什么从n号点能搜到的点最终是能到n号点的点?
A:反向存图,对于无向边和正常存图是一样的,毕竟都要存两遍,。
而对于有向边,比如正常是从u到v的一条有向边,反向存图就存成从v到u的一条边,所以我们从n号点搜,能搜到的点都是实际上能到n号点的点。
实在不会自己可以动手画一画~

然后,重点来了,为啥我们要用spfa?
用dis数组表示到达当前的点所经过的一路水晶球的最低价格
跑一遍spfa,如果dis[ed[i].to]>dis[queue[st](这个是当前点),就更新并入队。
注意,spfa初始时要将所有刚才两遍bfs跑出来的点都扔进队列里面!否则只从1号点开始万一能到达的点都比1的dis小就gameover了
最后跑完spfa ,用每个点本身卖出的价值减去它的dis就ok了,然后取一个max,任务完成

好了,重点都讲完了,上代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node
{
	int to;
	int next;
};
node ed[1000005],ed2[1000005];
int queue[1000005],vis[100005],vis2[100005],dis[100005],vale[100005];
int head[100005],head2[100005],cnt=0,n,m,cnt2=0,ans=0;
void addnode(int u,int v)
{
	cnt++;
	ed[cnt].to =v;
	ed[cnt].next =head[u];
	head[u]=cnt;
}
void addnode2(int u,int v)
{
	cnt2++;
	ed2[cnt2].to =v;
	ed2[cnt2].next =head2[u];
	head2[u]=cnt2;
}
int st=0,end=0;
void bfs1(int s)
{
	end++;
	queue[end]=s;
	while(st<end)
	{
		st++;
		vis[queue[st]]=1;
		for(int i=head[queue[st]];i;i=ed[i].next)
		{
			if(!vis[ed[i].to])
			{
				end++;
				queue[end]=ed[i].to;
			}
		}
	}
}
void bfs2(int s)
{
	st=0,end=0;
	end++;
	queue[end]=s;
	while(st<end)
	{
		st++;
		vis2[queue[st]]=1;
		for(int i=head2[queue[st]];i;i=ed2[i].next)
		{
			if(!vis2[ed2[i].to])
			{
				end++;
				queue[end]=ed2[i].to;
			}
		}
	}
}
void spfa(int s)
{
	st=0;
	end=0;
	for(int i=1;i<=n;i++)
	{
		if(vis[i])
		{
			end++;
			queue[end]=i;
			vis2[i]=1;	
		}
	}
	while(st<end)
	{
		st++;
		vis2[queue[st]]=0;
		for(int i=head[queue[st]];i;i=ed[i].next)
		{
			if(vis[ed[i].to])
			{
				if(dis[ed[i].to]>dis[queue[st]])
				{
					dis[ed[i].to]=dis[queue[st]];
					if(!vis2[ed[i].to])
					{
						end++;
						queue[end]=ed[i].to;
						vis2[ed[i].to]=1;
					}
				}
			}
		}
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&dis[i]);
		vale[i]=dis[i];
	}
	int a,b,c;//正反双向存图 
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&a,&b,&c);
		if(c==1)
		{
			addnode(a,b);
			addnode2(b,a);
		}
		else
		{
			addnode(a,b);
			addnode(b,a);
			addnode2(a,b);
			addnode2(b,a);
		}
	}
	bfs1(1);
	bfs2(n);
	for(int i=1;i<=n;i++)
	{
		//cout<<"i="<<i<<" 1 "<<vis[i]<<" n "<<vis2[i]<<endl;
		if(vis[i]&&vis2[i])
			 vis[i]=1;
		else vis[i]=0;
	 } 
	 memset(vis2,0,sizeof(vis2));
	spfa(1);
	for(int i=1;i<=n;i++)
		ans=max(ans,vale[i]-dis[i]);
	printf("%d",ans);	
	return 0;
 } 

本文到此结束,如有不懂欢迎评论!
感谢各位神犇对本蒟蒻的支持!qwq

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值