GYM102155A A.Ability Draft 状压DP 博弈
题意
有两个阵营共(2 imes n)个英雄,每个英雄可以选择(s)个普通技能和(1)个终极技能。
两个阵容都希望自己的技能总和尽量大对方的尽量小,问最终的 阵容一 的和减去 阵容二的和
[1 leq n leq 5,1 leq s leq 3
]
分析
看数据范围容易想到状压DP
(dp[i][j][k])表示前(i)个选择,(j,k)表示选择已经选择终极技能的情况
以开始的做法是从第1个开始枚举是否选择终极技能,然后往下递推,但很容易发现hack这样会走到很离谱的情况,于是想到这样的正确性可能不对,正确的应该是类似搜索那样从后往前DP
为了实现方便可以采取记忆化搜索(好像实现起来更烦)
据大佬口胡还可以用min-max搜索(待补)
代码
调了很久,实在是丑爆了
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int long long
ll rd(){
ll x = 0;
int f = 1;
char ch = getchar();
while(ch < '0' || ch > '9') {
if(ch == '-') f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
int dp[50][1 << 6][1 << 6];
int p[40];
int val1[100],val2[100];
int n,s;
int p1,p2;
int INF = 1e18;
// now = number of person haven't choose
// now1 = current position of normal
// now2 = current postion of specail
//
int get1(int x){
if(x == INF) return -INF;
return x;
}
int get2(int x){
if(x == -INF) return INF;
return x;
}
int add(int x,int y){
if(x == INF) return INF;
if(x == -INF) return -INF;
return x + y;
}
int dfs(int now,int msk1,int msk2,int now1,int now2){
if(now == 2 * n * (s + 1)) return 0;
if(now1 > 2 * n * s || now2 > 2 * n) return INF;
if(dp[now][msk1][msk2] != INF && dp[now][msk1][msk2] != -INF) return dp[now][msk1][msk2];
int ans;
int ok;
if(p[now] <= n) ok = (msk1 >> (p[now] - 1)) & 1;
else ok = (msk2 >> (p[now] - n - 1)) & 1;
ok ^= 1;
ok |= (now2 >= 2 * n);
int okk = (now1 >= 2 * n * s);
if(p[now] <= n) ans = max(max(-INF,ok ? -INF : add(get1(dfs(now + 1,msk1 ^ (1 << (p[now] - 1)),msk2,now1,now2 + 1)),val2[now2])),okk ? -INF : add(get1(dfs(now + 1,msk1,msk2,now1 + 1,now2)) , val1[now1]));
else {
ans = min(min(INF,ok ? INF : add(get2(dfs(now + 1,msk1,msk2 ^ (1 << (p[now] - n - 1)),now1,now2 + 1)), - val2[now2])),okk ? INF : add(get2(dfs(now + 1,msk1,msk2,now1 + 1,now2)), -val1[now1]));
}
//cout << "now = " << now << " msk1 = "<< msk1 << " msk2 = " << msk2 << " now1 = " << now1 << " now2 = " << now2 << " ok = " << ok << " okk = " << okk << "ans = " << ans << '
';
return dp[now][msk1][msk2] = ans;
}
signed main(){
n = rd();
s = rd();
for(int i = 0;i < 2 * n * (s + 1);i++)
p[i] = rd();
//reverse(p,p + 2 * n * (s + 1));
p1 = rd();
for(int i = 0;i < p1;i++)
val1[i] = rd();
p2 = rd();
for(int i = 0;i < p2;i++)
val2[i] = rd();
for(int i = 0;i < 2 * n * (s + 1);i++){
int f = p[i] <= n ? -INF : INF;
for(int k = 0;k < (1 << 5);k++)
for(int j = 0;j < (1 << 5);j++)
dp[i][k][j] = f;
}
sort(val1,val1 + p1,greater<int>());
sort(val2,val2 + p2,greater<int>());
cout << dfs(0,(1 << n) - 1,(1 << n) - 1,0,0);
}
JianglyGG的代码
#include <bits/stdc++.h>
constexpr int N = 5, S = 3, T = 2 * N * (S + 1), INF = 1e9;
int n, s, pu, ps;
int p[T], vs[36], vu[12], dp[T + 1][1 << (2 * N)], ok[T + 1][1 << (2 * S)];
int main() {
std::srand(std::chrono::steady_clock::now().time_since_epoch().count());
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n >> s;
for (int i = 0; i < 2 * n * (s + 1); ++i) {
std::cin >> p[i];
--p[i];
}
std::cin >> ps;
for (int i = 0; i < ps; ++i)
std::cin >> vs[i];
std::cin >> pu;
for (int i = 0; i < pu; ++i)
std::cin >> vu[i];
std::sort(vs, vs + ps, std::greater<>());
std::sort(vu, vu + pu, std::greater<>());
ok[2 * n * (s + 1)][(1 << (2 * n)) - 1] = 1;
for (int i = 2 * n * (s + 1) - 1; i >= 0; --i) {
for (int j = 0; j < (1 << (2 * n)); ++j) {
int c = __builtin_popcount(j);
ok[i][j] = ok[i + 1][j] | ok[i + 1][j | 1 << p[i]];
if (!ok[i][j] || c > i)
continue;
if (p[i] < n) {
dp[i][j] = std::max(ok[i + 1][j] ? dp[i + 1][j] + vs[i - c] : -INF, (~j >> p[i] & 1) && ok[i + 1][j | 1 << p[i]] ? dp[i + 1][j | 1 << p[i]] + vu[c] : -INF);
} else {
dp[i][j] = std::min(ok[i + 1][j] ? dp[i + 1][j] - vs[i - c] : INF, (~j >> p[i] & 1) && ok[i + 1][j | 1 << p[i]] ? dp[i + 1][j | 1 << p[i]] - vu[c] : INF);
}
}
}
std::cout << dp[0][0] << "
";
return 0;
}