Luogu P1073 最优贸易
在图上进行一次买,一次卖,使最后的获利最大。
可以看出,在一个节点x上,有两种选择:不买卖;买(当前没买)或卖(当前买了)。
把没买的状态记为状态x1,买了记为状态x2,买完又卖了记为状态x3。
在每个点上,可以由x1转到x2,x2转到x3,并且操作是一次性的。
考虑分层最短路,dis[x]表示到点x的花费。
把x1-x2连边,边权为w[x];x2-x3连边,边权为-w[x]。
再把相邻的点x1-y1,x2-y2,x3-y3分别连边,边权为0。
最后跑最短路就可以了。因为有负权边,所以要用SPFA。
代码如下
#include<cstdio> #include<iostream> #include<cmath> #include<cstring> #define MogeKo qwq #include<queue> using namespace std; const int maxn = 2e6; const int INF = 0x3f3f3f3f; int n,m,x,y,z,cnt; int head[maxn],to[maxn],nxt[maxn],val[maxn]; int w[maxn],dis[maxn]; bool vis[maxn]; void add(int x,int y,int z) { to[++cnt] = y; nxt[cnt] = head[x]; head[x] = cnt; val[cnt] = z; } void addedge(int x,int y){ add(x,y,0); add(x+n,y+n,0); add(x+n+n,y+n+n,0); } void spfa(int s) { queue <int> q; dis[s] = 0; q.push(s); vis[s] = true; while(!q.empty()) { int u = q.front(); q.pop(); vis[u] = false; for(int i = head[u]; i; i = nxt[i]) { int v = to[i]; if(vis[v])continue; if(dis[u] + val[i] >= dis[v])continue; dis[v] = dis[u] + val[i]; q.push(v); vis[v] = true; } } } int main() { scanf("%d%d",&n,&m); for(int i = 1; i <= n; i++) { scanf("%d",&w[i]); add(i,i+n,w[i]); add(i+n,i+n+n,-w[i]); dis[i] = dis[i+n] = dis[i+n+n] = INF; } for(int i = 1; i <= m; i++) { scanf("%d%d%d",&x,&y,&z); addedge(x,y); if(z == 2) addedge(y,x); } spfa(1); printf("%d",max(0,-dis[n+n+n])); return 0; }