题意:
给定 \(n,m\) 表示存在 \(n\) 个宝箱和 \(m\) 把钥匙,第 \(i\) 把钥匙需要 \(b_i\) 元,第 \(i\) 个宝箱内部有 \(a_i\) 元。
现在进行一场游戏,Bob 是本场游戏的玩家,而 Alice 则是场景布置者,Alice 可以给每个宝箱上一些锁(第 \(j\) 种锁需要第 \(j\) 种钥匙打开)
如果 Bob 可以购买一些钥匙,然后打开一些宝箱,使得 Bob 的收益大于 \(0\),那么 Bob 就赢得了游戏,反之 Alice 获得了胜利。
现在 Alice 打算布置宝箱上的锁,第 \(i\) 个宝箱上放置第 \(j\) 种锁的花费为 \(c_{i,j}\),请帮助 Alice 找到一种布置锁的方案,使得花费最小,且 Alice 将取得胜利。
\(n,m\le 6,a_i,b_i\le 4,c_{i,j}\le 10^7\)
特别的,一个箱子上可以放置若干把锁,Bob 需打开所有锁才能获得内部的钱。
分析:
先考虑如果给定了所有宝箱的布置锁的方案求 Bob 所得到的最大利益。
考虑网络流,连边 \(S \rightarrow i(w=a_{i}),\ i \rightarrow j(w=+\infty),\ j \rightarrow T(w=b_{j})\),其中 \(i\) 表示箱子,\(j\) 表示锁。那么最大利益即为 \((\sum a_{i})-Mincut\),具体证明见 P2762 太空飞行计划问题。
这题显然不能枚举所有所有宝箱的布置锁的方案,那怎么做呢?
如果想要 Bob 获利为 \(0\),即必须让他割 \(S\) 到所有箱子的边。
这题的突破口在于网络中的最大流量应使 \(S\) 到所有箱子的边达到满流,否则最小割一定不是 \(S\) 到所有箱子的边。
知道了这点就很简单了,注意到 \(a_{i},b_{i} \le 4\),随便 dp 一下即可。
比如我们可以记 \(f_{i,j,k,l}\) 表示考虑到第 \(i\) 个箱子(该箱子残余流量为 \(l\)),此时所有钥匙的残余流量集合为 \(j\),目前在考虑是否要走 \(i \rightarrow k\) 这条边。
状态数是 \(6 \times 5^6 \times 6 \times 4=2.5 \times 10^5\),不会爆空间。
需要注意的是考虑是否要走 \(i \rightarrow k\) 这条边时,我们还需要枚举其流量,不能无脑全流。
时间复杂度为 \(O(nmw^2 \times (w+1)^m)\)。
代码:
#include<bits/stdc++.h>
#define int long long
#define N 10
using namespace std;
int n, m, ans = 1e18;
int a[N], b[N], c[N][N], cnt;
vector<int>now, z, p[100005], r;
int f[8][16000][7][7];
map<vector<int>, int>num;
void dfs(int k) {
if(k > m) {
p[++cnt] = z;
num[z] = cnt;
for(int i = 1; i <= n + 1; i++)
for(int j = 1; j <= m; j++)
for(int l = 0; l <= 4; l++)
f[i][cnt][j][l] = 1e18;
return;
}
for(int i = 0; i <= b[k]; i++) {
z.push_back(i);
dfs(k + 1);
z.pop_back();
}
}
void upd(int &x, int y) {
x = min(x, y);
}
signed main() {
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 1; i <= m; i++) cin >> b[i], now.push_back(b[i]);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++) cin >> c[i][j];
dfs(1);
f[1][num[now]][1][a[1]] = 0;
for(int i = 1; i <= n; i++) {
for(int k = 1; k <= m; k++) {
for(int j = 1; j <= cnt; j++) {
for(int l = 0; l <= a[i]; l++) {
if(k != m) {
upd(f[i][j][k + 1][l], f[i][j][k][l]);
int use = min(l, p[j][k - 1]);
r = p[j];
for(int v = 1; v <= use; v++) {
r[k - 1] -= v;
upd(f[i][num[r]][k + 1][l - v], f[i][j][k][l] + c[i][k]);
r[k - 1] += v;
}
}
else {
if(l == 0) upd(f[i + 1][j][1][a[i + 1]], f[i][j][k][l]);
else if(p[j][k - 1] >= l) {
r = p[j];
r[k - 1] -= l;
upd(f[i + 1][num[r]][1][a[i + 1]], f[i][j][k][l] + c[i][k]);
}
}
}
}
}
}
for(int i = 1; i <= cnt; i++) ans = min(ans, f[n + 1][i][1][0]);
if(ans == 1e18) cout << -1;
else cout << ans;
return 0;
}
标签:宝箱,int,题解,Alice,times,Chests,Keys,Bob,rightarrow
From: https://www.cnblogs.com/xcs123/p/18056677