[NOIP2009提高组]最优贸易

题目:洛谷P1073、Vijos P1754、codevs1173。

题目大意:有n点m边的图,边分有向和无向。每个点有一个价格,用这个价格可以买入或卖出一个东西。一个人从1出发,要到n,途中可以买入卖出一次(可以不买入卖出),问最多能赚多少钱?

解题思路:首先,要在点i卖出,买入的最低价格为点1到点i的所有路径中,经过的价格最低的一个点的价格。这个用一个BFS扫一遍即可。

然后,有些点是不能到n的(因为有有向边),这些点不能卖出。要求哪些点能到n,只需建反向图,再在反向图里从n开始BFS一遍即可。最后即可得出答案。

C++ Code:

#include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
vector<int>G[100005],G2[100005];
int n,m,money[100005],minE[100005];
bool used[100005],ok[100005];
queue<int>q;
inline void addedge(int from,int to,int isTwo){
	G[from].push_back(to);
	if(isTwo==2)G[to].push_back(from);
}
inline void addINVedge(int from,int to,int isTwo){
	G2[to].push_back(from);
	if(isTwo==2)G2[from].push_back(to);
}
void BFS(int t){
	q.push(t);
	memcpy(minE,money,sizeof minE);
	memset(used,0,sizeof used);
	used[t]=1;
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(int i=0;i<G[u].size();++i){
			int v=G[u][i];
			if(minE[v]>minE[u]){
				minE[v]=minE[u];	
				if(!used[v]){
					used[v]=true;
					q.push(v);
				}
			}else
			if(!used[v]){
				used[v]=true;
				q.push(v);
			}
		}
	}
}
void BFS2(int t){
	q.push(t);
	memset(ok,0,sizeof ok);
	ok[t]=true;
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(int i=0;i<G2[u].size();++i){
			int v=G2[u][i];
			if(!ok[v]){
				ok[v]=true;
				q.push(v);
			}
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)scanf("%d",money+i);
	while(m--){
		int u,v,two;
		scanf("%d%d%d",&u,&v,&two);
		addedge(u,v,two);
		addINVedge(u,v,two);
	}
	BFS(1);
	BFS2(n);
	int ans=0;
	for(int i=1;i<=n;++i)
	if(ok[i]){
		int p=money[i]-minE[i];
		if(p>ans)ans=p;
	}
	printf("%d
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/Mrsrz/p/7565065.html