题目大意
有 n ( ≤ 200 ) n(\le 200) n(≤200) 张手牌,第 i i i 张手牌有 d i d_i di 点攻击力,但打出后会强制从当前手牌中随机弃掉 a i ( ≤ 4 ) a_i (\le 4) ai(≤4) 张牌,求最优策略下至少能打出多少攻击力。
思路
将手牌按
a
i
a_i
ai 分为 4 类,分别按
d
i
d_i
di 排序。由于要考虑最坏情况,因此默认弃掉攻击力最强的那批牌,只需记录当前每类用掉了前多少张牌,以及有多少张牌需要弃。状态数为
5
0
4
×
5
50^4\times5
504×5 。
其实根本不需要剪枝。
代码
#include <bits/stdc++.h>
#define rep(i, l, r) for (int i = l; i <= r; ++i)
using namespace std;
typedef long long ll;
const int N = 205;
const int inf = 0x7fffffff;
int T;
int n;
int a[5][N];
int tot[5];
int dp[51 * 51 * 51 * 51 * 5];
int id(int i1, int i2, int i3, int i4, int todel) {
int ret = i1;
ret = ret * (tot[2] + 1) + i2;
ret = ret * (tot[3] + 1) + i3;
ret = ret * (tot[4] + 1) + i4;
ret = ret * 5 + todel;
return ret;
}
int dfs(int i1, int i2, int i3, int i4, int todel) {
int ind = id(i1, i2, i3, i4, todel);
if (dp[ind] != -1) return dp[ind];
int ret = 0;
if (todel) {
int tmp = inf;
if (i1) tmp = min(tmp, dfs(i1 - 1, i2, i3, i4, todel - 1));
if (i2) tmp = min(tmp, dfs(i1, i2 - 1, i3, i4, todel - 1));
if (i3) tmp = min(tmp, dfs(i1, i2, i3 - 1, i4, todel - 1));
if (i4) tmp = min(tmp, dfs(i1, i2, i3, i4 - 1, todel - 1));
if (tmp == inf) tmp = 0; // nothing to delete
return dp[ind] = tmp;
}
// i1
if (i1) {
int tmp = dfs(i1 - 1, i2, i3, i4, 1);
ret = max(ret, tmp + a[1][i1]);
}
// i2
if (i2) {
int tmp = dfs(i1, i2 - 1, i3, i4, 2);
ret = max(ret, tmp + a[2][i2]);
}
// i3
if (i3) {
int tmp = dfs(i1, i2, i3 - 1, i4, 3);
ret = max(ret, tmp + a[3][i3]);
}
// i4
if (i4) {
int tmp = dfs(i1, i2, i3, i4 - 1, 4);
ret = max(ret, tmp + a[4][i4]);
}
return dp[ind] = ret;
}
int main() {
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
rep(i, 1, 4) tot[i] = 0;
rep(i, 1, n) {
int x, ap;
scanf("%d%d", &x, &ap);
a[ap][++tot[ap]] = x;
}
rep(i, 1, 4) { sort(a[i] + 1, a[i] + tot[i] + 1); }
rep(i, 0,
(tot[1] + 1) * (tot[2] + 1) * (tot[3] + 1) * (tot[4] + 1) * 5 - 1)
dp[i] = -1;
dp[0] = 0;
int ans = dfs(tot[1], tot[2], tot[3], tot[4], 0);
printf("%d\n", ans);
}
return 0;
}