Chests and Keys 题解

题意:

给定 \(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;
}
posted @ 2024-03-06 15:12  小超手123  阅读(9)  评论(0编辑  收藏  举报