[题解] 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;
}
希望本篇文章可以帮助到大家