poj1900 Game

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 }
View Code

 

posted @ 2016-06-02 19:08  lyxxx  阅读(404)  评论(0编辑  收藏  举报