吴昊品游戏核心算法(新年特别篇)—— 称硬币游戏AI(朴素枚举)(POJ 2692)

  吴昊继续,首先还是来一段人生感悟吧——在我很小的时候,大约是初中的时候,我知道要让自己的人生混得好就应该思考许多问题,所以我喜欢思考;十几年过去 了,我领悟到要让自己的人生混得好就应该不思考许多问题,但是,我仍然喜爱思考,无论是对是错,总之,我思考过,并有自己的感悟,我觉得这乃是一件很好的 事情,所以,我接着思考,并决定在寒假之前完成自己的《吴昊品历史人物》第一季(Round 1--Round 10)

 在我年轻时,我所做的事,十中有九都是失败的,为了不甘于失败,我便十倍努力于工作。——萧伯纳

 

   如图所示,此为一个普通的天平,但是没有标度,也就是说,我们根据其只能判断出是轻了,重了,还是一样的。我们目前有N个硬币(这里应该好更改的,这里以经 典的问题为主,就是12个硬币有一个是假的却又不知道轻重,这样的话,称三次是可以称出来的,只要方法得当即可,当然,这里已经假设作者已经设计出了正确 的方案,如何设计呢?见下文)

  站在逻辑推理角度来思考

 12个硬币和一个天平,硬币中有一个是假的,它与真币质量不同。请你用天平称这些硬币,在三次之内得到两个结果:(1)哪个硬币是假币?(2)假币比真币轻还是重?

答案:先取出八个硬币,在天平两边各放四个。(第一次)

1.       一样重。

说明假币在剩下四个里面。给嫌疑币编号ABCD,未编号硬币为O。接着这样称:ABC_OOO(第二次)

1)一样重。

说明最后一个是假币。然后这样称:D_O(第三次)。这样就知道假币是轻了还是重了。

2)左面轻(重)

说明ABC里面有一个假币,且比真币轻(重)。然后这样称:A_B(第三次)。

①一样重。说明C轻(重),是假币。

②不一样重。说明轻(重)的是假的。

 

2.       不一样重。(不妨设左面轻)

说明假币在这八个硬币里面,编号为ABCDEFGH。接着这样称:AOOO_EBCD(第二次)

1)一样重。

说明FGH里有一个重了,是假币。然后这样称:F_G(第三次)。

①一样重。说明H是假币,重。

②不一样重。说明重的是假币。

2)左面轻。

说明A轻或E重。然后这样称:A_O(第三次)就知道A是不是假的。

3)右面轻。

说明BCD里面有一个轻了,是假币。然后这样称:B_C(第三次)。

①一样重。说明D是假币,轻。

②不一样重。说明轻的是假币。

 

 站在算法的角度来思考:

 

 这样做,倒是给计算机节省了不少负担的说,但是,这里发挥了人类的智慧,却没有利用计算机的特长(高效率的计算能力),那么,对于计算机来说还是不划算 的。况且,当硬币的数目更改之后,我们又要重新找到一些方法,这样来说的话,十分麻烦。于是,站在计算机的角度,我们利用朴素枚举来解决问题,这样做可以 增强普遍性,硬币的数量N一改,也很容易更换代码。由于时间复杂度为O(N^2log3(n)),所以暴力一下也是可以接受的。

 我最开始的时候误以为时间复杂度是O(N)的,后来想通了,并在第二季中给出了解释,这里搬回来。

 作为首映式,首先,纠正一下以前的错误吧,在吴昊系列的 “称硬币游戏AI”中,我说的时间复杂度是O(n),实际上有问题,首先,确实将所有的硬币都遍历了一遍,但是,每遍历一个硬币的时候,strchr函数 将遍历一个天平上的所有硬币,这样是O(n/2),还没有完,因为称的次数与硬币的数目是正相关的,那么,至少是O(n^2)以上,一篇论文中用信息熵来 解决这个问题!,得到如果N个硬币有一个(可能没有)为假币,至少要称量K=log(2N+1)/log3次,那么,可以得到时间复杂度的上限为 O(n^2log3n)


 

  Solve:

 利用isHeavy(char x)和isLight(char x)对每一种可能的硬币进行枚举(或者可以称为暴力吧),得到合适的哪一个。

 

 1  #include<stdio.h>
 2  //涉及到字符串的一些处理
 3  #include<string.h>
 4  
 5  /*以字符串数组存储称量的结果。每次称量的时候,天平左右最多有6枚
 6    硬币。因此,字符串的长度为7,最后一位存储字符串的结束符'\0',
 7    便于程序代码中使用字符串的操作函数
 8  */
 9  char left[3][7],right[3][7],result[3][7];
10  
11  //对每一个硬币进行试探
12  //x比真币重的猜测是否成立?如果成立则输出
13  bool isHeavy(char x)
14  {
15    int i;
16    for(i=0;i<3;i++)
17    {
18      switch(result[i][0])
19      {
20        case 'u'if(strchr(left[i],x)==NULL) return false;
21                  break;          
22        case 'e'if(strchr(right[i],x)!=NULL||strchr(left[i],x)!=NULL) return false;
23                  break;
24        case 'd'if(strchr(right[i],x)==NULL) return false;
25                  break;                  
26      }
27    }
28    return true;     
29  }
30  
31  //x比真币轻的猜测是否成立?如果成立则输出
32  bool isLight(char x)
33  {
34    int i;
35    //判断选择的那一个硬币与三次结果是否矛盾
36    for(i=0;i<3;i++)
37    {
38      //strchr函数为逐个字符串比对
39      switch(result[i][0])
40      {
41        case 'u'if(strchr(right[i],x)==NULL) return false;
42                  break;          
43        case 'e'if(strchr(right[i],x)!=NULL||strchr(left[i],x)!=NULL) return false;
44                  break;
45        case 'd'if(strchr(left[i],x)==NULL) return false;
46                  break;  
47      }                
48    }
49    return true;     
50  }
51  
52  int main()
53  {
54    int n;
55    char c;
56    //读入案例的数目
57    scanf("%d",&n);
58    while(n--)
59    {
60      for(int i=0;i<n;i++)
61      {
62        //对于字符串的读入不需要&取址
63        scanf("%s%s%s",left[i],right[i],result[i]);        
64      }          
65      for(c='A';c<='L';c++)
66      {
67        if(isLight(c))
68        {
69          printf("%c is the counterfeit coin and it is light.\n",c);
70          break;              
71        }                     
72        if(isHeavy(c))
73        {
74          printf("%c is the counterfeit coin and it is heavy.\n",c);
75          break;               
76        }
77      }
78      //如果还需要找到一个结论的话,需要标注flag,这里略去
79    }
80    return 0;    
81  }

 
 

posted on 2013-02-28 12:17  吴昊系列  阅读(343)  评论(0编辑  收藏  举报

导航