CF1728D Letter Picking 题解
CF1728D Letter Picking
思路:
区间 DP+博弈。
因为每次取完都放到第一个,所以最后一定是 Alice 赢或者平局。为什么呢?当最后只剩两个时,若两个不同,则 Alice 可以取较小的那个取胜,若相同,Alice 也会在前面的操作中选小的。而且 \(n\) 是偶数,所以不存在 Alice 取的字符串比 Bob 长的情况。
阅读题目和数据范围,我们猜测是区间 DP。设 \(f_{l,r}\) 表示剩余区间 \([l,r]\) 时,是平局还是 Alice 取胜。因为字典序按位比较,我们一次让双方都操作。
怎样判断这一次是平局?
- Alice 取 \(s_l\):
那么 Bob 只能选 \(s_{l+1}\) 或 \(s_r\) 使得答案尽可能为平局。
若 Bob 取 \(s_{l+1}\),那么只有 \(s_{l+1}\) 和 \(s_l\) 相同,并且 \(f_{l+2,r}\) 为平局时答案为平局。
若 Bob 取 \(s_{r}\),那么只有 \(s_{r}\) 和 \(s_l\) 相同,并且 \(f_{l+1,r-1}\) 为平局时答案为平局。
任一满足一种即可在 Alice 取 \(s_l\) 时平局。 - Alice 取 \(s_r\):
那么 Bob 只能选 \(s_{r-1}\) 或 \(s_l\) 使得答案尽可能为平局。
若 Bob 取 \(s_{r-1}\),那么只有 \(s_{r-1}\) 和 \(s_r\) 相同,并且 \(f_{l,r-2}\) 为平局时答案为平局。
若 Bob 取 \(s_{r}\),那么只有 \(s_{r}\) 和 \(s_l\) 相同,并且 \(f_{l+1,r-1}\) 为平局时答案为平局。
任一满足一种即可在 Alice 取 \(s_r\) 平局。
对于 Alice 的两种取法,Bob 都要有应对方法。所以这两种要都满足才能取得平局。
一点小优化:因为每次双方都操作,所以 \(f_{l,r}\) 只有在区间长度为偶数时有意义,那我们可以在枚举区间长度时只枚举偶数。
code:
#include<bits/stdc++.h>
using namespace std;
const int N=2002;
int T;
int f[N][N];//1表示Alice获胜,0表示平局。
string s,t;
bool check(int l,int r){
int p1,p2,p3,p4;
p1=p2=p3=p4=0;
if(s[l]==s[l+1]&&f[l+2][r]==0) p1=1;
if(s[l]==s[r]&&f[l+1][r-1]==0) p2=1;
if(s[r]==s[r-1]&&f[l][r-2]==0) p3=1;
if(p1+p2>=1&&p2+p3>=1) return 1;//两种情况各至少满足一个
return 0;
}
int main(){
scanf("%d",&T);
while(T--){
cin>>s;
int sl=s.size();
s='#'+s;
for(int i=1;i<=sl;i++){
for(int j=1;j<=sl;j++) f[i][j]=0;//初始化。
}
for(int i=1;i<sl;i++) {
if(s[i]==s[i+1]) f[i][i+1]=0;
else f[i][i+1]=1;
}
for(int len=4;len<=sl;len+=2){
for(int l=1;l+len-1<=sl;l++){
int r=l+len-1;
if(check(l,r)) f[l][r]=0;
else f[l][r]=1;
}
}
if(f[1][sl]==1) printf("Alice\n");
else printf("Draw\n");
}
return 0;
}