Loading

「清新题精讲」MagicalHats

SRM549 - MagicalHats

Statement

给定 \(n\times m\) 的地图,. 表示空地,H 表示帽子,帽子下会有不超过 \(1\) 个硬币。

接下来有 numGuesses 次操作,每一次魔法师将会改变硬币的位置,然后你将选择 \(1\) 个帽子,获得的价值为帽子底下硬币的价值(如果无硬币,则价值为 \(0\)

魔法师希望你获得的价值尽可能小,而你希望尽可能大,求最后的最大价值。

Solution

观察一些性质:如果获得的硬币数为 \(x\),则价值为硬币从小到大排序后前 \(x\) 小的和。

问题进而转化为求最多能拿到多少个硬币。考虑对于 \(1\) 个帽子有几种状态:①未被掀开 ②被掀开但无硬币 ③被掀开且有硬币

所以,由于帽子数最大只有 \(13\),那么可以考虑用三进制存储下所有的状态并 DP 转移即可。

转移:

  • 边界条件 \(1\):如果硬币全部放置完,则只需要判断最后的摆放是否合法即可。
  • 边界条件 \(2\):如果选择次数全部使用完,则只需要判断是否存在 \(1\) 种方案放置剩余的硬币使得情况合法。
  • 非边界转移:\(f_{mask}=\max(f_{mask},\min(f_{mask|i_1},f_{mask|i_2}+1))\)。这里 \(i_1\) 指第 \(i\) 个帽子被掀开但无硬币,\(i_2\) 指被掀开且有硬币。由于魔法师希望值尽可能小,所以取 \(\min\)。但是你希望尽可能取大,所以外面取 \(\max\)

Code

#include <bits/stdc++.h>
#define fi first
#define se second

using namespace std;

typedef pair<int, int> PII;
typedef long long LL;

const int N = 2e6 + 10;

int n, m, tot;
PII pos[14];
int f[N];
class MagicalHats {
public:
	int dp(int st, int cnt, int k) {
		if (f[st] != -1) return f[st];
		if (!k) {
			std::vector<int> lin(n, 0), col(m, 0);
			for (int i = 0; i < tot; i ++)
				if ((st / (int)pow(3, i)) % 3 == 2) lin[pos[i].fi] += 2, col[pos[i].se] += 2;
				else lin[pos[i].fi] ++, col[pos[i].se] ++;
			for (int i = 0; i < n; i ++) if (lin[i] & 1) return f[st] = 2e9;
			for (int i = 0; i < m; i ++) if (col[i] & 1) return f[st] = 2e9;
			return 0;
		}
		if (!cnt) {
			for (int i = 0; i < tot; i ++)
				if ((st / (int)pow(3, i)) % 3 == 0 && dp(st + 2 * pow(3, i), 0, k - 1) == 0)
					return f[st] = 0;
			return f[st] = 2e9;
		}
		int res = 0;
		for (int i = 0; i < tot; i ++)
			if ((st / (int)pow(3, i)) % 3 == 0)
				res = max(res, min(dp(st + pow(3, i), cnt - 1, k), dp(st + 2 * pow(3, i), cnt - 1, k - 1) + 1));
		return f[st] = res;
	}
	int findMaximumReward(vector<string> g, vector<int> w, int turn) {
		memset(f, -1, sizeof f);
		n = g.size(), m = g[0].size();
		for (int i = 0; i < n; i ++)
			for (int j = 0; j < m; j ++)
				if (g[i][j] == 'H')
					pos[tot ++ ] = {i, j};
		int res = dp(0, turn, w.size()), ans = 0;
		if (res > w.size()) return -1;
		sort(w.begin(), w.end());
		for (int i = 0; i < res; i ++)
			ans += w[i];
		return ans;
	}
};
posted @ 2024-05-29 20:24  E-Syrus  阅读(5)  评论(0编辑  收藏  举报