2022 CCPC 绵阳区域赛 A

A - Ban or Pick, What's the Trick

博弈论 dp。

\(dp_{i,j,k}\) 表示第 \(i\) 轮 A 选了 \(j\) 个,B 选了 \(k\) 个。

对 A 来说,当前如果要选应该选第 \(i - k + j + 1\) 个位置, \(i - k\) 为对手 ban 掉的,\(j\) 为自己已经选的,同理,B 要选也是第 \(i - j + k + 1\) 个,但这里是把 \(i\) 拆成 \(2n\) 轮了,所以对于 A 来说,这一轮里 B 要选的位置就是 A 可以选择 ban 掉的位置。

A 要取最大化,B 要最小化,注意取 dp 值时的比较。

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

constexpr int N = 1E5 + 10;
int dp[2 * N][11][11];

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int n, m;
	cin >> n >> m;

	vector<int> a(n + 1), b(n + 1);
	for (int i = 1; i <= n; i += 1) {
		cin >> a[i];
	}
	for (int i = 1; i <= n; i += 1) {
		cin >> b[i];
	}

	sort(a.begin() + 1, a.end(), greater<>());
	sort(b.begin() + 1, b.end(), greater<>());

	const int inf = 2E9;
	for (int i = 1; i <= 2 * n; i += 1) {
		for (int j = 0; j <= m; j += 1) {
			for (int k = 0; k <= m; k += 1) {
				dp[i][j][k] = (i % 2 ? 1 : -1) * inf;
			}
		}
	}

	auto dfs = [&](auto &&self, int i, int j, int k)->int{
		if (i > 2 * n) {
			return 0;
		}
		if (dp[i][j][k] != inf && dp[i][j][k] != -inf) {
			return dp[i][j][k];
		}

		int posa = i / 2 - k + j + 1;
		int posb = i / 2 - j + k + 1;

		int ans;
		if (i & 1) {
			ans = -inf;
			if (posa <= n && j != m) {
				ans = max(ans, self(self, i + 1, j + 1, k) + a[posa]);
			}
			ans = max(ans, self(self, i + 1, j, k));
		}
		else{
			ans = inf;
			if (posb <= n && k != m) {
				ans = min(ans, self(self, i + 1, j, k + 1) - b[posb]);
			}
			ans = min(ans, self(self, i + 1, j, k));
		}
		return dp[i][j][k] = ans;
	};

	cout << dfs(dfs, 1, 0, 0) << "\n";

	return 0;
}
posted @ 2025-09-15 14:59  Ke_scholar  阅读(18)  评论(0)    收藏  举报