[COCI2021-2022#1] Kamenčići
[COCI2021-2022#1] Kamenčići
题目链接:[P7928 COCI2021-2022#1] Kamenčići - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
容易想到一个状态 \(dp_{l,r,a,b,0/1}\) 表示还剩 \(l,r\) 这个区间没拿, Alice 拿了 \(a\) 块红石头,Bob 拿了 \(b\) 块红石头,且当前是 Alice/Bob 选择拿石头,最后的胜者是谁。那么转移就是暴力转移。
\[dp_{l,r,a,b,0}=dp_{l+1,r,a,b+(s[l]=='C'),1}==1 \&\& dp_{l,r-1,a,b+(s[r]=='C'),1}==1
\]
类似的,我们可以写出 \(opt=1\) 时的转移式。
注意到,由于总共的红石头数不变,一个区间内红石头数也不变,所以我们只要知道其中一个人拿了几块石头就可以用前缀和算出另外一个人拿了几块石头。
那么就优化了一维,时空复杂度为 \(O(n^3)\)
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 355;
bool Small;
int n,K,suml[MAXN],sumr[MAXN];
int dp[MAXN][MAXN][MAXN][2];
char s[MAXN];
bool Sunny;
int dfs(int l,int r,int k,int opt)
{
if(suml[l-1]+sumr[r+1]-k==K) return opt;
if(dp[l][r][k][opt]!=-1) return dp[l][r][k][opt];
int tmp1,tmp2;
tmp1=dfs(l+1,r,suml[l-1]+sumr[r+1]-k,opt^1);
tmp2=dfs(l,r-1,suml[l-1]+sumr[r+1]-k,opt^1);
int ans;
if(opt) ans=(tmp1==1||tmp2==1);
else ans=(tmp1==1&&tmp2==1);
return dp[l][r][k][opt]=ans;
}
int main()
{
memset(dp,-1,sizeof dp);
scanf("%d %d",&n,&K);
scanf("%s",s+1);
for(int i=1;i<=n;++i)
suml[i]=suml[i-1]+(s[i]=='C');
for(int i=n;i>=1;--i)
sumr[i]=sumr[i+1]+(s[i]=='C');
if(dfs(1,n,0,1)) printf("DA\n");
else printf("NE\n");
return 0;
}
路漫漫其修远兮,吾将上下而求索。