博主的 BiBi 时间
博主在外面浪了多天已经退化成原始人的智商了。
Description
感觉某谷题面漏掉了一些细节,英语辣鸡的博主看了好久。。。
- 两个队伍都使用最优策略。
-
p
i
c
k
\mathtt{pick}
pick 与
b
a
n
\mathtt{ban}
ban 的顺序严格按照题目输入的顺序。
这不是废话?
Solution
感觉很多出题人都喜欢设置一些不会用到的限制/条件。。。
对于这道题, m i s s p i c k \mathtt{misspick} misspick 和 m i s s b a n \mathtt{missban} missban 操作实际是没有用的。
- m i s s p i c k \mathtt{misspick} misspick:这个操作只有队伍进行 p i c k \mathtt{pick} pick 时才能使用,显然如果单纯 p i c k \mathtt{pick} pick 我会选择当前能选的最强的英雄,而这个操作肯定是从当前能选的英雄中乱选,不会比选这时最强的英雄更优。
- m i s s b a n \mathtt{missban} missban:在证明之前,我们要确定 p i c k \mathtt{pick} pick 和 b a n \mathtt{ban} ban 都只会在最强的 m m m 个英雄之中选。然后我们可以将 b a n \mathtt{ban} ban 分成两类:一类是阻止另一队伍选择强的英雄,另一类就是混吃等死。第一类肯定是不会使用 m i s s b a n \mathtt{missban} missban 的,而后面那一类只是帮助占满 m m m 空间没有影响,所以是否 m i s s b a n \mathtt{missban} missban 都没有影响。
好的我们接下来开始 D P \mathtt{DP} DP, i i i 为 m m m 位二进制数,表示第 j j j 强的英雄是否被操作( p i c k , b a n \mathtt{pick,ban} pick,ban 均可), f [ i ] f[i] f[i] 表示状态 i i i 下队伍一力量减去队伍二力量的最大值。
如何处理对于队伍一和队伍二分别的最优选择(显然全取 max \max max 是队伍二都是憨憨状态下)?当前操作为队伍一时取 max \max max,为队伍二时取 min \min min。
然后,这样是过不了样例的一个都过不了.jpg。为什么?假设第一轮队伍一
p
i
c
k
\mathtt{pick}
pick,第二轮队伍二
p
i
c
k
\mathtt{pick}
pick,一共就俩英雄
l
y
x
,
w
w
l
\mathtt{lyx,wwl}
lyx,wwl,其中
w
w
l
\mathtt{wwl}
wwl 特别强,把
l
y
x
\mathtt{lyx}
lyx 虐哭了。虽然队伍一肯定选
w
w
l
\mathtt{wwl}
wwl,但是还是有
f
[
(
01
)
2
]
,
f
[
(
10
)
2
]
f[(01)_2],f[(10)_2]
f[(01)2],f[(10)2] 两个状态(
l
y
x
\mathtt{lyx}
lyx 被象征性地选入),然后到了队伍二,它发现有可乘之机,赶忙选了
w
w
l
\mathtt{wwl}
wwl,从队伍一那个象征性选
l
y
x
\mathtt{lyx}
lyx 的状态转移,这样就错了。解决方案就是从
m
m
m 到
1
1
1 地进行操作,使在前面的操作有了主动权。
Code
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 105, inf = (1 << 30);
int n, m, a[N], t[25], f[1 << 20];
char op[25];
int read() {
int x = 0, f = 1; char s;
while((s = getchar()) > '9' || s < '0') if(s == '-') f = -1;
while(s >= '0' && s <= '9') x = (x << 1) + (x << 3) + (s ^ 48), s = getchar();
return x * f;
}
int main() {
n = read();
for(int i = 0; i < n; ++ i) a[i] = read();
sort(a, a + n, greater <int> ());
m = read();
for(int i = 0; i < m; ++ i) {
scanf("\n");
op[i] = getchar(); t[i] = read();
}
for(int i = 1; i < (1 << m); ++ i) {
int j = m - __builtin_popcount(i);
if(t[j] == 1) f[i] = -inf;
else f[i] = inf;
for(int k = 0; k < m; ++ k)
if(i & (1 << k)) {
int tmp = (op[j] == 'p' ? a[k] : 0);
if(t[j] == 1) f[i] = max(f[i], f[i ^ (1 << k)] + tmp);
else f[i] = min(f[i], f[i ^ (1 << k)] - tmp);
}
}
printf("%d\n", f[(1 << m) - 1]);
return 0;
}