吴昊品游戏核心算法 Round 7(特刊)—— 猜数字系列 第二弹(模拟)(HDOJ 1172)

 

如图所示,是用JAVA的Swing图形界面做出的猜数字游戏(还有一款基于android系统和IOS系统的,其界面也给出吧),其专业名字叫Bulls and Cows,历史上可以追溯到20世纪中期,是兴起于英国的益智类小游戏。其规则如下:

 

 

标准规则

通常由两个人玩,一方出数字,一方猜。出数字的人要想好一个没有重复数字的4位数(该规则实际上是可以进行改良的),不能让猜的人知道。猜的人就可以开始猜。每猜一个数字,出数者就要根据这个数字给出几A几B,其中A前面的数字表示位置正确的数的个数,而B前的数字表示数字正确而位置不对的数的个数。

如正确答案为 5234,而猜的人猜 5346,则是 1A2B,其中有一个5的位置对了,记为1A,而3和4这两个数字对了,而位置没对,因此记为 2B,合起来就是 1A2B。接着猜的人再根据出题者的几A几B继续猜,直到猜中(即 4A0B)为止。

猜测次数限制

猜数字游戏通常设有猜测次数的上限。根据计算机测算,如果采用严谨的猜测策略,任何数字最多7次就可猜出(即达到 4A0B)。值得注意的是,在有些地方把次数上限定义为最多几次猜测以后就可以肯定数字是几,但这时或还需要再猜一次才能得到 4A0B 的结果。

数码与数位的变化

标准的猜数字游戏由10个数码(0-9)和4个数位组成。可以通过变化数码或数位来丰富游戏。例如,可以使用9个数码玩4个数位的游戏。

允许重复数字

猜数字游戏的一种变体允许重复的数码。这种规则的游戏被称为 Mastermind[1]。其规则大致为:

除了上面的规则外,如果有出现重复的数字,则重复的数字每个也只能算一次,且以最优的结果为准。例 如,如正确答案为 5543,猜的人猜5255,则在这里不能认为猜测的第一个5对正确答案第二个,根据最优结果为准的原理和每个数字只能有一次的规则,两个比较后应该为 1A1B,第一个5位子正确,记为1A;猜测数字中的第三个5或第四个5和答案的第二个5匹配,只能记为1B。当然,如果有猜5267中的第一个5不能与 答案中的第二个5匹配,因此只能记作1A0B。

  我们首先来模拟出这款游戏的判胜负程序,并在此基础之上,对于不同的游戏策略,给出不同的AI。

  以下的Source来自HDOJ的1172.(杭电的OJ确实帮了我不少的忙啊!)

Problem Description——猜数字游戏是gameboy最喜欢的游戏之一。游戏的规则是这样的:计算机随机产生一个四位数(当然,这里的设定是产生的四位数的数字之间是允许重复的),然后玩家猜这个四位数是什么。每猜一个数,计算机都会告诉玩家猜对几个数字,其中有几个数字在正确的位置上。
比 如计算机随机产生的数字为1122。如果玩家猜1234,因为1,2这两个数字同时存在于这两个数中,而且1在这两个数中的位置是相同的,所以计算机会告 诉玩家猜对了2个数字,其中一个在正确的位置。如果玩家猜1111,那么计算机会告诉他猜对2个数字,有2个在正确的位置。
现在给你一段GameBoy(GB)与计算机的对话过程,你的任务是根据这段对话确定这个四位数是什么。

 Input—— 输入数据有多组。每组的第一行为一个正整数N(1<=N<=100),表示在这段对 话中共有N次问答。在接下来的N行中,每行三个整数A,B,C。gameboy猜这个四位数为A,然后计算机回答猜对了B个数字,其中C个在正确的位置 上。当N=0时,输入数据结束。

 

Output——每组输入数据对应一行输出。如果根据这段对话能确定这个四位数,则输出这个四位数,若不能,则输出"Not sure"。

Solve:


/*
    该模拟算法的亮点如下:
    (1)考虑到各个位数之间的比较有些麻烦,可以采取利用字符数组存储的方法。
    (2)基于"桶排序"的思想,分别装填0--9出现的次数,这样就省去了分离的麻烦。
    (3)这种“数组里面套数组”的用法,很灵活,也很新颖,让人眼前一亮。
 
*/
 
 
 #include<stdio.h>
 
 typedef struct res //分别根据输入导出的判定数a和b
 {
   int a,b;       
 }Res;
 
 Res judge(int an,int gu)//分别是假想的答案和所猜测的数
 {
   int i;
   int ans[4];
   int guess[4];
   Res r;
   r.a=0,r.b=0;
   int n[10]={0};//类似于"桶排序"的思想,分别给出0--9出现的次数
   for(i=3;i>=0;i--)
   {
     ans[i]=an%10;
     an=an/10;
     guess[i]=gu%10;
     gu=gu/10;                
   }   
   for(i=0;i<4;i++)//对于在位置上和数字上都正确的判定
   {
     n[ans[i]]++;
     if(ans[i]==guess[i])
       r.b++;               
   }
   for(i=0;i<4;i++)//对于在位置上不正确而在数字上正确的判定
   {
     if(n[guess[i]]>0)
     {
       n[guess[i]]--;
       r.a++;                
     }                                                       
   }
   return r;
 }
 
 int main()
 {
   int N;
   int i,m;
   int n;
   int f;
   Res re;
   int key,text[101][3];//每一组数都有一个四位数和两个判断数
   freopen("in.txt","r",stdin);//文件输入
   while(scanf("%d",&N)&&N!=0//N为0的时候表征退出
   {
     for(i=0;i<N;i++)
     {
       scanf("%d%d%d",&text[i][0],&text[i][1],&text[i][2]);               
     }                           
     f=0;//利用变量f排除两个答案的情况
     for(i=1000;i<10000;i++)
     {
       n=0;//查看满足的次数,如果恰好满足N次,则判定成功找到一个
       for(m=0;m<N;m++)
       {
         re=judge(i,text[m][0]);
         if(re.a==text[m][1]&&re.b==text[m][2])
           n++;
         else
           break;               
       }
       //找出符合条件的可行解        
       if(n==N)
       {
         key=i;
         f++;       
       }
       //如果有两个以上的解,则判定是不可行的    
       if(f>1)
         break;        
     }
     if(f==1) printf("%d\n",key);
     else printf("Not sure\n");
   }
   return 0;   
 }

posted on 2013-02-27 21:21  吴昊系列  阅读(500)  评论(0编辑  收藏  举报

导航