[luogu4459][BJOI2018]双人猜数游戏(DP)
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。但是这样只能保证已经有一方已知答案,同时还要保证的是另一方当这方说”知道了“之后也知道了答案,而说之前还不知道,这需要另一个类似的暴力拆分解决。
所以我们分别模拟两人的思维,后期每个点大约跑几分钟。
1 #include<cmath> 2 #include<cstdio> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 using namespace std; 6 7 const int N=2010; 8 int s,t; 9 bool flag,f[N][N][20]; 10 char a[10]; 11 12 bool chk1(int x,int y,int t){ 13 int num=x*y,len=sqrt(x*y),x1=0,y1=0,cnt=0; 14 rep(i,s,len) 15 if(num%i==0 && ((!f[i][num/i][t-1])||(!t))) 16 x1=i,y1=num/i,cnt++; 17 if(cnt==1 && x1==x && y1==y) return 1; else return 0; 18 } 19 20 bool chk2(int x,int y,int t){ 21 int num=x+y,len=(x+y)/2,x1=0,y1=0,cnt=0; 22 rep(i,s,len) 23 if((!f[i][num-i][t-1])||(!t)) 24 x1=i,y1=num-i,cnt++; 25 if(cnt==1 && x1==x && y1==y) return 1; else return 0; 26 } 27 28 bool chk3(int x,int y,int t){ 29 int num=x*y,len=sqrt(x*y),x1=0,y1=0,cnt=0; 30 rep(i,s,len) 31 if(num%i==0 && (f[i][num/i][t]&&(t<2||(!f[i][num/i][t-2])))) 32 x1=i,y1=num/i,++cnt; 33 if(cnt==1 && x1==x && y1==y) return 1; else return 0; 34 } 35 36 bool chk4(int x,int y,int t){ 37 int num=x+y,len=(x+y)/2,x1=0,y1=0,cnt=0; 38 rep(i,s,len) 39 if(f[i][num-i][t]&&(t<2||(!f[i][num-i][t-2]))) 40 x1=i,y1=num-i,++cnt; 41 if(cnt==1 && x1==x && y1==y) return 1; else return 0; 42 } 43 44 int main(){ 45 freopen("guess.in","r",stdin); 46 freopen("guess.out","w",stdout); 47 scanf("%d",&s); scanf("%s",a+1); scanf("%d",&t); 48 if (a[1]=='A') flag=0; else flag=1; 49 rep(i,0,t){ 50 flag^=1; 51 rep(j,s,1000) rep(k,s,1000){ 52 if(i>=2)f[j][k][i]=f[j][k][i-2]; 53 f[j][k][i]|=flag?chk1(j,k,i):chk2(j,k,i); 54 } 55 } 56 int sum=2*s,x=0,y=0; 57 while (1){ 58 rep(i,s,sum/2){ 59 x=i; y=sum-i; flag=f[x][y][t]; 60 if (!flag) continue; 61 rep(j,0,t-1) if(f[x][y][j]){ flag=false; break; } 62 if (!flag) continue; 63 if(((t&1)&&a[1]=='A')||((!(t&1))&&a[1]=='B')) flag=chk3(x,y,t); else flag=chk4(x,y,t); 64 if (!flag) continue; 65 printf("%d %d\n",x,y); return 0; 66 } 67 sum++; 68 } 69 return 0; 70 }