循环dp、spfa、松弛操作
题意:
分析:
其实,我标签里的所谓循环dp其实也就是类似分层图。不过是另一种角度看待问题而已。
我认为对于状态相互转移的图论问题应该适用。
首先,让我们回顾一下Bellman-Ford算法。
Bellman-Ford在试图解决最短路问题时总结了一个状态转移公式:
d[i] = min(d[k] + cost[k,i] k为i的相邻节点) d[i] 为节点i到节点1的最短距离
我们很容易就能发现如果所给的图不是一个DAG的话,那么这个dp公式是会陷入死循环的!
假设i和j构成一个环i需要j推出,j需要i推出。。。。。。
那么,Bellman-Ford是如何解决这个问题的呢?
松弛操作!
通过松弛操作,我么们成功破解了循环dp问题!!前提是没有负环
那么对于这题,我们该如何做呢?
给出状态方程:
dp[i][j] i节点在j状态下距离s的最短路
dp[i][0] 指到i节点已经买了
dp[i][1] 指到了i节点已经卖了
买的操作加cost[i],卖的操作减cost[i]
那么给出状态转移公式:
dp[i][0] = min(min(dp[k][0] k为其邻节点),cost[i])
dp[i][1] = min(min(dp[k][1] k为其邻节点),dp[i][0] - cost[i])
这就是一个循环dp,我们可以采用松弛操作,利用队列优化(spfa算法)因为有负边
但是,这题在进行状态转移有特殊的地方
我们设置dp[i][1] = dp[i][0] = inf作为默认
又dp[s][0] = cost[s]作为初始状态,然后由此状态进行更新。
dp[k][0] 更新dp[k][1],dp[i][0]
dp[k][1] 更新dp[i][1]
但是在dp[k][0] 更新dp[i][0]的过程中要考虑cost[i]的,这点万分注意!
可能说的不大好理解,可以看看我下面的代码。
或许循环dp的说法有点抽象,这题用分层图是很好解的,可以看看其他的题解。有一篇写的非常好
代码如下:
#include<iostream> #include<algorithm> #include<vector> #include<queue> using namespace std; typedef pair<int, int> pii; const int max_n = 1e5 + 100; const int max_m = 5e5 + 100; const int inf = 2e9; int n;int m; struct edge { int to, next; }; int cost[max_n]; int head[max_n]; edge E[max_m * 4]; int cnt; int d[max_n][2]; bool used[max_n][2]; void addedge(int from,int to) { E[cnt].to = to; E[cnt].next = head[from]; head[from] = cnt++; } int spfa(int s) { queue<pii> que; for (int i = 0;i <= n;i++) { used[i][1] = used[i][0] = 0; d[i][1] = d[i][0] = inf; }d[s][0] = cost[s];que.push({ s,0 }); while (!que.empty()) { pii p = que.front();que.pop(); int pos = p.first;int state = p.second; used[pos][state] = false; if (state == 1) { for (int i = head[pos];i;i = E[i].next) { if (d[E[i].to][1] > d[pos][1] && !used[E[i].to][1]) { used[E[i].to][1] = true; que.push({ E[i].to,1 }); }d[E[i].to][1] = min(d[E[i].to][1], d[pos][1]); } } else if (state == 0) { if (d[pos][1] > d[pos][0] - cost[pos] && !used[pos][1]) { used[pos][1] = true; que.push({ pos,1 }); }d[pos][1] = min(d[pos][1], d[pos][0] - cost[pos]); for (int i = head[pos];i;i = E[i].next) { int cmp = min(d[pos][0], cost[E[i].to]); if (d[E[i].to][0] > cmp && !used[E[i].to][0]) { used[E[i].to][0] = true; que.push({ E[i].to,0 }); }d[E[i].to][0] = min(d[E[i].to][0], cmp); } } } return d[n][1]; } int main() { ios::sync_with_stdio(0); cin >> n >> m; for (int i = 1;i <= n;i++)cin >> cost[i]; for (int i = 1;i <= m;i++) { int type; int u, v; cin >> u >> v >> type; addedge(u, v); if (type == 2)addedge(v, u); } cout << -spfa(1) << endl; }