算法问题《Card Game》的数学模型和Java实现

来源:互联网 发布:查网站域名是否被注册 编辑:程序博客网 时间:2024/06/03 20:23

 问题描述:
 有4张红色的牌和4张蓝色的牌,主持人先拿任意两张,再分别在A、B、C三人额头上贴任意两张牌,
 A、B、C三人都可以看见其余两人额头上的牌,看完后让他们猜自己额头上是什么颜色的牌,
 A说不知道,B说不知道,C说不知道,然后A说知道了。
 请教如何推理,A是怎么知道的。
 如果用程序,又怎么实现呢?

 

这道题摘自高手July的牛贴《横空出世,席卷Csdn:记微软等100题系列数次被荐[100题维护地址]》。前两天刚看到,觉得很有意思就仔细花时间想了一下。这个game其实不难,难的是如何建立起数学模型,并用算法来模拟。

 

1. 先给问题设计一个数学模型:
设红色的牌一张算1分,蓝色的牌一张算10分。这样最初发牌之前,4张红牌和四张蓝牌,总分值是44分。对某一个人来说,他拿到牌以后可能得到分数如下:
1.1) 一红一蓝就是11分;
1.2) 俩红就是20分;
1.3)俩蓝就是2分。

ABC三人可能拿到分值分别为SA, SB, SC,那么{SA,SB, SC}的所有可能情况是{11, 20, 2}的任意组合。
先定义一下:private int[] m_c42 = {20, 11, 02};

 

2. 然后依据这个模型进行分析:

(步骤一,筛除所有A一眼就能猜出来的组合)首先对任意两个人(AB)来说,如果AB分别拿了2张同色的,而且AB拿的都是红色,这样C看到他们的牌后,是可以猜到自己的牌的。AB拿走牌后,只剩下四张蓝牌了,C只可能有2张蓝牌。于是乎,我们可以得到第一个判断条件:任意两个人的分数值之和不可以是4或者40,否则另一个人一下就猜出来了。

public boolean isGuessible1(int p_p1, int p_p2) {

            return (p_p1 == 2 && p_p2 == 2) || (p_p1 == 20 && p_p2 == 20);
}

 

根据第一个条件,我们可以对2个人分数的所有组合情况做一次筛选。

ArrayList<int[]> l_candidateList = new ArrayList<>();

for(int l_i = 0; l_i < m_c42.length; l_i++) {
   for(int l_j = 0; l_j < m_c42.length; l_j++) {
      if(!isGuessible1(m_c42[l_i], m_c42[l_j])) {
         l_candidateList.add(new int[]{m_c42[l_i], m_c42[l_j]});
      }
   }
}


这段代码结束时,我们排除了所有只要看一眼就能猜出来的组合。

(步骤二,筛除A猜不到,但BC猜到了的组合) 然后怎么继续下去呢?有两句话很关键:A猜不到,B也猜不到,C也猜不到。刚才我们做完第二步,已经保证了“A猜不到”。那怎么继续保证B和C也猜不到呢?我们来看个例子:
假设A一开始看到的是这种情况:{SA=??, SB=20, SC=02}。那么首先SB和SC是能通过第二步的筛选的,所以A一开始猜不出来。接下来我们需要看一下下面两种假设:
  3.1)如果SA=02,那么B看到的就是{SA=02, SB=??, SC=02},这样B就猜得出来。
  3.2)如果SA=20,那么C看到的就是{SA=20, SB=20, SC=??},这样C就能猜出来。
所以,但是A知道B和C没猜到,所以SA只能等于11!条件函数是如下:

public boolean isGuessible2(int p_p1, int p_p2) {
   int l_nonGuessible = 0;
   for (int l_i = 0; l_i < m_c42.length; l_i++) {
      boolean l_guessible2 = isGuessible1(p_p1, m_c42[l_i]) || isGuessible1(p_p2, m_c42[l_i]);
      if(!l_guessible2) {
         l_nonGuessible ++;
      }
   }
   return l_nonGuessible == 1;
}


把第二步筛选过的组合,再用新的条件重筛一下。你会发下,A看到某几个组合的时候,也会抓瞎:)

希望大家多给意见!


原创粉丝点击