TonyYin's Blog

Back

题意#

一个游戏,有 nn 座城堡,每局对战有两名玩家,每名玩家有 mm 个士兵,每人可以向第 ii 个城堡派遣 aia_i 名士兵,士兵总数满足:a1+a2++anma_1+a_2+\cdots+a_n\leq m.

二人争夺这些城堡,如果一名玩家向城堡 ii 派遣的士兵数严格大于对手士兵数的两倍,那么这名玩家占领了这座城堡,获得 ii 分。

现给定 ss 名玩家及其各自的派兵策略,你需要选定一个固定的派兵策略,最大化你在 ss 场游戏中的总得分。

1s100,1n100,1m200001\leq s\leq 100, 1\leq n\leq 100, 1\leq m\leq 20000.

分析#

考虑DP。

fi,jf_{i,j} 表示:对于前 ii 个城堡共派出 jj 个士兵;设 ai,ja_{i,j} 表示:在城堡 ii,玩家 jj 的出兵数量。

对每个城堡,把各个玩家在该城堡的出兵数量,按升序排序。

这样一来,若你在城堡 ii 的出兵数量大于 2×ai,j2\times a_{i,j},则你可以在城堡 ii 获得 i×ji\times j 的得分。

于是对每个赢得城堡 ii,枚举要战胜前 kk 个玩家,分别转移即可:

f[i][j]=maxk=1s{0,jai,k×21<0f[i][jai,k×21]+ki,jai,k×210f[i][j]=\max_{k=1}^s \left\{ \begin{array}{lr} 0, &j-a_{i,k}\times 2 - 1 < 0\\ f[i][j-a_{i, k}\times2-1]+k\cdot i, &j-a_{i,k}\times2-1\geq 0 \end{array} \right.

属于分组背包模型。

代码#

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 108, MAXM = 20008, MAXS = 108;
int s, n, m;
int dp[MAXM], a[MAXN][MAXS];//a[i][j]表示第i个城堡,第j个玩家
int main() {
	scanf("%d%d%d", &s, &n, &m);
	for(int i = 1; i <= s; i++)
		for(int j = 1; j <= n; j++)
			scanf("%d", &a[j][i]);
	for(int i = 1; i <= n; i++)
		sort(a[i] + 1, a[i] + s + 1);
	for(int k = 1; k <= n; k++) {
		for(int i = m; i > 0; i--) {
			for(int j = 1; j <= s; j++) {
				if(i >= a[k][j] * 2 + 1) {
					dp[i] = max(dp[i], dp[i - a[k][j] * 2 - 1] + k * j);
				}
			}
		}
	}
	cout << dp[m] << endl;
	return 0;
}
cpp
【洛谷-P5322】[BJOI2019]排兵布阵
https://www.tonyyin.top/blog/oi-solution/p5322
Author TonyYin
Published at October 28, 2022
Comment seems to stuck. Try to refresh?✨