• 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
  • 相关阅读:
    bzoj4152-[AMPPZ2014]The_Captain
    bzoj3209-花神的数论题
    [模板] 数位dp
    [西安交大附中集训] 自积
    [模板] 后缀数组
    [模板] 哈希表
    [西安交大附中集训] d6 删边(cip)
    java 发布订阅
    Spring Boot使用@Async实现异步调用:自定义线程池
    上传文件到服务器或者直接输出到输出流
  • 原文地址:https://www.cnblogs.com/liuaohan/p/5554126.html
Copyright © 2020-2023  润新知