题解:P9902 『PG2』模拟最大流
题意:很简短了,不再赘述。
做法:乍一看是很难做的,但是我们观察到 \(k\le 7\),然后我们考虑到其实我们可以将最大流转为最小割,那么就很自然可以考虑状压 dp。
割的要求是点 \(1\) 和点 \(n\) 不连通,所以我们可以考虑状压连通性。因为注意到所有边 \((u, v)\) 满足 \(|u-v| \le k\),所以我们其实只用考虑点 \(u-k,u-k+1\cdots u-1\) 这些点与 \(1\) 的连通性,再考虑这些点与 \(u\) 的连通性就可以得到 \(1\) 与 \(u\) 的连通性继续进行状压。
所以我们考虑状态为 \(dp_{i, s}\) 代表我们考虑到了前 i 个点,\(i-k + 1, i - k + 2\cdots i\) 这 \(k\) 个点与 \(1\) 连通的状态被压缩为 \(s\)。
那么我们考虑对 \(dp_{i+1}\) 进行转移,我们枚举是否可以被 \(1\) 到达。如果可以,那么我们就不需要割掉前面这些点对点 \(i+1\) 的这些边,当然也有可能根本就连通不了,但是没有关系,这样的非法情况不会使我们的答案更优,因为我们肯定会在考虑他被割掉的时候得到一个不劣于认为他是与 \(1\) 连通的答案。
如果不可以,那么我们就需要割掉 \(i - k + 1, i - k + 2\cdots i\) 这 \(k\) 个点与 \(i+1\) 之间的边,这个东西可以提前预处理出来。
计算答案的时候,我们对 \(dp_{n, 2t}\) 计算答案即可。
然后就可以得到一个 \(O(n2^k)\) 的做法了,感觉并没有什么难度。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int n, m, k, g[maxn][130], dp[maxn][130], all, lwb[130];
int to[maxn][10];
signed main() {
ios::sync_with_stdio(false);
cin >> n >> m >> k;
for (int i = 1; i <= m; i++) {
int u, v, w; cin >> u >> v >> w;
if(u != v)
to[v][v - u - 1] += w;
}
for (int i = 1; i < (1 << k); i++) {
lwb[i] = lwb[i >> 1] + 1;
if((i & 1))
lwb[i] = 0;
}
all = (1 << k) - 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j < (1 << k); j++)
g[i][j] = g[i][j ^ (1 << lwb[j])] + to[i][lwb[j]];
}
memset(dp, 0x3f, sizeof(dp));
for (int s = 0; s < (1 << k); s++)
if(s & 1)
dp[1][s] = 0;
for (int i = 1; i < n; i++) {
for (int s = 0; s < (1 << k); s++) {
// cout << i << " asd" << s << " " << g[i + 1][s] << endl;
dp[i + 1][(s << 1 | 1) & all] = min(dp[i + 1][(s << 1 | 1) & all], dp[i][s]);
dp[i + 1][(s << 1) & all] = min(dp[i + 1][(s << 1) & all], dp[i][s] + g[i + 1][s]);
}
}
int ans = 2e9;
for (int s = 0; s < (1 << k); s++)
if(s % 2 == 0)
ans = min(ans, dp[n][s]);
cout << ans << endl;
return 0;
}

浙公网安备 33010602011771号