[luogu4459][BJOI2018]双人猜数游戏(DP)

时间:2024-09-28 10:03:44

https://zhaotiensn.blog.luogu.org/solution-p4459

从上面的题解中可以找到样例解释,并了解两个人的思维方式。

A和B能从“不知道”到“知道”的唯一情况,就是根据已知条件(也就是已经说的”不知道“次数)排除手上数的所有其它合法拆分方案。

那么,设dp[i][j][k]表示,两个数分别为i,j,当前已经说了k次不知道,这个数是否能确定(也就是某方知道了答案)。

那么有两种转移

dp[i][j][k]|=dp[i][j][k-2]  一轮之前就已经知道了这轮肯定也知道。

对于B: dp[i-s][j+s][k-1]在s取遍所有合法取值时,只有s=0是false,其余全为true。也就是i+j的所有其它合法拆分方案在说了k-2次不知道后,A都应该说知道答案了,唯独这种方案仍不知道,那么B就肯定可以确定这个数了。

对于A: 同理,将i*j拆分即可。

考虑什么情况下满足题设条件,即说了t次”不知道“后双方都知道答案了。

那么就是dp[i][j][t-1]为false而dp[i][j][t]为true。但是这样只能保证已经有一方已知答案,同时还要保证的是另一方当这方说”知道了“之后也知道了答案,而说之前还不知道,这需要另一个类似的暴力拆分解决。

所以我们分别模拟两人的思维,后期每个点大约跑几分钟。

 #include<cmath>
#include<cstdio>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
using namespace std; const int N=;
int s,t;
bool flag,f[N][N][];
char a[]; bool chk1(int x,int y,int t){
int num=x*y,len=sqrt(x*y),x1=,y1=,cnt=;
rep(i,s,len)
if(num%i== && ((!f[i][num/i][t-])||(!t)))
x1=i,y1=num/i,cnt++;
if(cnt== && x1==x && y1==y) return ; else return ;
} bool chk2(int x,int y,int t){
int num=x+y,len=(x+y)/,x1=,y1=,cnt=;
rep(i,s,len)
if((!f[i][num-i][t-])||(!t))
x1=i,y1=num-i,cnt++;
if(cnt== && x1==x && y1==y) return ; else return ;
} bool chk3(int x,int y,int t){
int num=x*y,len=sqrt(x*y),x1=,y1=,cnt=;
rep(i,s,len)
if(num%i== && (f[i][num/i][t]&&(t<||(!f[i][num/i][t-]))))
x1=i,y1=num/i,++cnt;
if(cnt== && x1==x && y1==y) return ; else return ;
} bool chk4(int x,int y,int t){
int num=x+y,len=(x+y)/,x1=,y1=,cnt=;
rep(i,s,len)
if(f[i][num-i][t]&&(t<||(!f[i][num-i][t-])))
x1=i,y1=num-i,++cnt;
if(cnt== && x1==x && y1==y) return ; else return ;
} int main(){
freopen("guess.in","r",stdin);
freopen("guess.out","w",stdout);
scanf("%d",&s); scanf("%s",a+); scanf("%d",&t);
if (a[]=='A') flag=; else flag=;
rep(i,,t){
flag^=;
rep(j,s,) rep(k,s,){
if(i>=)f[j][k][i]=f[j][k][i-];
f[j][k][i]|=flag?chk1(j,k,i):chk2(j,k,i);
}
}
int sum=*s,x=,y=;
while (){
rep(i,s,sum/){
x=i; y=sum-i; flag=f[x][y][t];
if (!flag) continue;
rep(j,,t-) if(f[x][y][j]){ flag=false; break; }
if (!flag) continue;
if(((t&)&&a[]=='A')||((!(t&))&&a[]=='B')) flag=chk3(x,y,t); else flag=chk4(x,y,t);
if (!flag) continue;
printf("%d %d\n",x,y); return ;
}
sum++;
}
return ;
}