[题解] P1073 最优贸易 SPFA 分层图

[题解] P1073 最优贸易 SPFA 分层图

洛谷题目链接

牛客题目链接

本题在最短路的基础上,本题需要考虑在何时购买,何时卖出,因为只可以买入卖出1次,在每个位置时一共有三种情况:没有买入,买入但没有卖出,已经卖出,重点是其中的转换

1.同状态之间转换 建一条长度为0的边

2.没有买入->买入但没有卖出 建一条长度为负买入价值的边,表示买入

3.买入但没有卖出->已经卖出 建一条长度为卖出价值的边,表示卖出

然后就可以建立一个分层图,因为存在负边,所以用SPFA求解

如果有无向边,往返各建一条有向边即可

建图代码如下:

while(m--){
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		add_edge(x,y,0),add_edge(x,y+n,-c[x]),add_edge(x+n,y+2*n,c[x]);
		add_edge(x+n,y+n,0),add_edge(x+2*n,y+2*n,0); 
		if(z==2){  //双向边
			add_edge(y,x,0),add_edge(y,x+n,-c[y]),add_edge(y+n,x+2*n,c[y]);
			add_edge(y+n,x+n,0),add_edge(y+2*n,x+2*n,0);
		}
	}
	int T=n*3+1;
	add_edge(n,T,0),add_edge(n*3,T,0);  //终点

然后进行SPFA

void SPFA(){
	for(int i=2;i<=n;i++)dis[i]=-INF;
	Q.push(1),in_q[1]=1;
	while(Q.size()){
		int u=Q.front();Q.pop();
		in_q[u]=0;
		for(int i=head[u];i;i=edge[i].nex){
			int v=edge[i].to;
			if(dis[v]<dis[u]+edge[i].w){
				dis[v]=dis[u]+edge[i].w;
				if(!in_q[v])Q.push(v),in_q[v]=1; 
			}
		}
	}
}

最终dis[T]就是答案

AC代码:

#include <cstdio>
#include <queue>
#include <algorithm>

using namespace std;

struct Edge{
	int to,w,nex;
	Edge(int to=0,int w=0,int nex=0):to(to),w(w),nex(nex){}
};

const int maxn=3e5+7,INF=1e9+7;
int n,m,c[maxn];
int dis[maxn],in_q[maxn];
int head[maxn],edge_cnt;
Edge edge[maxn*5];

void add_edge(int x,int y,int w){
	edge[++edge_cnt]=Edge(y,w,head[x]);
	head[x]=edge_cnt;
}

queue<int> Q;
void SPFA(){
	for(int i=2;i<=n;i++)dis[i]=-INF;
	Q.push(1),in_q[1]=1;
	while(Q.size()){
		int u=Q.front();Q.pop();
		in_q[u]=0;
		for(int i=head[u];i;i=edge[i].nex){
			int v=edge[i].to;
			if(dis[v]<dis[u]+edge[i].w){
				dis[v]=dis[u]+edge[i].w;
				if(!in_q[v])Q.push(v),in_q[v]=1; 
			}
		}
	}
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)scanf("%d",&c[i]);
	while(m--){
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		add_edge(x,y,0),add_edge(x,y+n,-c[x]),add_edge(x+n,y+2*n,c[x]);
		add_edge(x+n,y+n,0),add_edge(x+2*n,y+2*n,0); 
		if(z==2){
			add_edge(y,x,0),add_edge(y,x+n,-c[y]),add_edge(y+n,x+2*n,c[y]);
			add_edge(y+n,x+n,0),add_edge(y+2*n,x+2*n,0);
		}
	}
	int T=n*3+1;
	add_edge(n,T,0),add_edge(n*3,T,0);
	n=T;
	SPFA();
	printf("%d",dis[T]);
	return 0;
}

希望本篇文章可以帮助到大家

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值