http://poj.org/problem?id=1900
一道有意思的很好的题目。
A,S,P三个人玩一个游戏。A先宣布一个正整数N(2<=N<=200),然后选择两个不同的正整数x,y(1<=x,y<=N),然后把x+y的值告诉S,把x*y的值告诉P。
接下来S和P轮流告诉对方自己现在是否知道这两个数是多少,S先开始。例如:
S和P都已知N=10,S知道x+y=9,P知道x*y=18。
S:我不知道x和y的值。
P:我也不知道。
S:我还不知道。
P:我仍然不知道。
S:我现在知道了,x=3,y=6.
给定N和M(两人总共说“我不知道”的次数,0<=M<=100),求所有可能的x和y。
乍一看,很神奇是吧?
我们来分析一下,3和6这个数据太大了(我刚开始分析了半天才看出了策略),先来看一些更简单的例子:
如果N=10,S知道x+y=3.可能的组合只有3=1+2. 毫无疑问x=1,y=2. 这样只要M=0次就能猜出来了。
如果N=10,x+y=8,x*y=7:
首先S知道所有可能的组合有8=2+6=3+5. S不知道到底是哪一种。
然后P知道所有可能的组合只有7=1*7. 因此x=1,y=7. 这样只要M=1次就猜出来了。
再如N=10,x+y=4,x*y=4.
首先S所有可能的组合为4=1+3=2+2. S还不知道。
然后P所有可能的组合为4=1*4=2*2. P也还不知道。
接下来又是S。他已经知道P在上一次没有猜出来,而如果x=1,y=3,那么P所知道的数应该为3,从而他应该在上一次就猜出来了。
所以4=1+3这种组合是错的。因此S知道,只有4=2+2一种可能。这样只要M=2次就可以猜出来了。
从以上例子我们可以得到一个结论:如果在第M次“不知道”后,某个人所知道的全部组合ALL中只有一种还没有猜出来(也就是说其余的所有组合形式都可以在M次之内猜出来),那么这一种就是答案无疑了。相反,如果在M次之后,仍然有多于一种情况都没有猜出来,显然也就无法决定到底是这些情况中的哪一种了。
根据这种策略,只要从m=0从小到大枚举,对每种x,y,根据M的奇偶性把x+y或x*y进行分解,看是否只有x,y这一种情况没有还猜出来。然后再M次之后,看所有xy的组合,那种可以在M次之后猜出来即可。
这道题的时限卡得很紧,有两点需要注意:
第一,如果某次在m次之后没有任何一个xy被更新的话,接下来m再怎么变大也不会有变动了(事实上有的状态无论多少次都猜不出来)。直接break
第二,x*y如果每次都分解一遍的话会被模和除运算卡掉。在开始预处理一下才行。
注:我这道题第一天看想出了正确解法,但是没注意到x和y不相等这个条件,得到的答案不对,所以搁置了两个星期。。今天拾起来再提。
刚开始WA掉了因为以为只有x=1,y=2这种情况是M=0的(其实N=10,x+y=19这种也是啊)。
1 #include<iostream> 2 #include<cstdio> 3 #include<string> 4 #include<cstring> 5 #include<algorithm> 6 #include<cmath> 7 using namespace std; 8 9 const int N = 205; 10 int n,f[N][N],tot[N*N]; 11 pair<int,int> fac[N*N][15]/*15试出来的,否则MLE*/; 12 13 int search(int a,int b,int dep) 14 {//搜索j,k是否要i次才能猜出来 15 int s = a+b,p = a*b,i,flag = 1; 16 if(dep%2==0) { 17 for(i = 1; i+i<s; i++) 18 if(i!=a && s-i<=n && f[i][s-i]>=dep) { 19 flag = 0; break; 20 } 21 if(flag) f[a][b] = dep; 22 } 23 else { 24 for(i = 1; i<=tot[p]; i++) 25 if(fac[p][i].first!=a && f[fac[p][i].first][fac[p][i].second]>=dep) { 26 flag = 0; break; 27 } 28 if(flag) f[a][b] = dep; 29 } 30 return f[a][b]==dep; 31 } 32 33 void preset() 34 { 35 for(int i = 1; i<n; i++) 36 for(int j = i+1; j<=n; j++) { 37 tot[i*j]++; 38 fac[i*j][tot[i*j]] = make_pair(i,j); 39 } 40 } 41 42 int main() 43 { 44 int i,j,k,m,ans = 0; 45 cin >> n >> m; 46 memset(f, 0x3f, sizeof(f)); 47 preset(); 48 for(i = 0; i<=m; i++) { 49 int flag = 0; 50 for(j = 1; j<=n; j++) 51 for(k = j+1; k<=n; k++) 52 if(f[j][k]>m) 53 flag |= search(j,k,i); 54 if(!flag) break; 55 } 56 for(i = 1; i<=n; i++) 57 for(j = i+1; j<=n; j++) 58 if(f[i][j]==m) ans++; 59 printf("%d\n", ans); 60 for(i = 1; i<=n; i++) 61 for(j = i+1; j<=n; j++) 62 if(f[i][j]==m) printf("%d %d\n", i,j); 63 return 0; 64 }