国王和100个囚犯

国王招来100个囚犯,对他们说:你们犯的是死罪,本应该将你们统统杀掉,但我慈悲为怀,给你们一次求生的机会。15分钟以后,你们将被关进一个有100间隔离牢房的监狱里,每人一间牢房,都与外界隔绝,什么也听不见、看不到,连时间都没法计算,更别说获得外界的任何信息。(送饭除外,但也是不规律的送)

这所监狱有一个院子,每天会随机(注意是完全随机)打开一间牢房的门,让那个囚犯到院子里来放风。院子里有一盏路灯,放风的囚犯可以控制它的开关,将它打开或是关闭。除囚犯之外,其他人都不会去碰开关。这盏灯会永远有充足的能源供应,如果灯泡坏了或是电路出了故障会马上修好,当然修理人员不会改变灯的状态(开或关)。

除了开关这盏灯,放风的囚犯放风时留下的任何其它痕迹都会在夜晚被清除干净(包括在灯上作的任何记号)。

牢房是完全封闭的,院子里的灯光在牢房里看不到。只有放风出到院子里的人才能看到。

好了现在我向你们提出一个要求,只要你们做到了,就可以全部获得释放: 若干天以后,你们中只要有任何一个人能够向我证明所有的人都曾到院子里去过,你们就全体释放。当然要有证据!因为我只会给你们一次机会,如果向我证明的那个人无法自圆其说,你们就全部砍头。所以,要珍惜这次机会。如果你们永远做不到我的要求,你们就全部关到死。

现在给你们15分钟商量你们的方案。15分钟以后,你们将被关进我刚才说的那个监狱,永远无法再交流。

 

 

初始思路:

第一个出去的人的任务:开灯,且只负责开灯。 其他所有人的任务:关灯,且只负责关灯,且只在自己从未关过灯的情况下才可以关灯

 

         这样第一个人经过无数次放风后,一共看到了99次关着的灯,他们就自由了

 

网友思路:

这个问题我有几点看法:
1、囚犯不知道时间,也就是说他们不知道自己会是第几个人。根据条件,他们更不知道是第几天。
2、囚犯随机的放风。这里可能出现同一囚犯放风多次的情况。
3、那个路灯是他们唯一交流的工具。
4、现在最大的问题是不知道灯的初始状态。

我的办法:
1、指定计数囚犯。100囚犯中指定一个唯一的计数囚犯。
2、规则:
(1)所有囚犯除计数囚犯外,当他是第1次放风的时候,如果灯是关着的则设置灯为开,以后再放风,不干涉灯的状态。但第1次放风时,如果灯是开着的,则自己认为自己没放过风。
(2)计数囚犯统计灯开的次数,统计之后将灯关闭。计数到99时,结束。

细节解释:当计数囚犯第1次出门,如果灯是开着的。会有3种情况:1、灯默认是开着的,他是第1个放风的。2、灯是默认开着的,其他囚犯没有去碰开关。3、灯初始是关着的,一放风囚犯将它改为开。
现在问题出现了,由于不知道灯的初始状态,所以在计数囚犯计第1个数时是有误差的,误差为1人。但如果,计数囚犯放风时看到灯是关着的,那就万事大吉了。


这个思路蛮不错的,但是我还有个疑问:
计数员关灯99次的时候,就说明大家都出来过了,如果灯最初是开着的,那么第一个将灯关闭的人肯定是计数员,计数员认为已经有一个人出来过了(其实还没人出来过),当出来到第98个的时候,计数员就已经数到99,认为大家都出来过了,然后大家都被拉出去砍头了。如果计数员考虑到这种情况,打算多数一个到100,当初始灯是关着的时候,他们就永远都出不去了,因为大家都只开一次,永远都到不了100.

 

这个思路蛮不错的,但是我还有个疑问:
计数员关灯99次的时候,就说明大家都出来过了,如果灯最初是开着的,那么第一个将灯关闭的人肯定是计数员,计数员认为已经有一个人出来过了(其实还没人出来过),当出来到第98个的时候,计数员就已经数到99,认为大家都出来过了,然后大家都被拉出去砍头了。如果计数员考虑到这种情况,打算多数一个到100,当初始灯是关着的时候,他们就永远都出不去了,因为大家都只开一次,永远都到不了100.

 

 

 

java实现的DEMO:

  1. import java.util.Random;   
  2.   
  3. public class Prisoner2 {   
  4.     /**  
  5.      * 犯人数目(大于1)  
  6.      */  
  7.     private static final int PRISONER_COUNT = 100;   
  8.   
  9.     /**  
  10.      * @param args  
  11.      */  
  12.     public static void main(String[] args) {   
  13.         int minYear = Integer.MAX_VALUE;   
  14.         int maxYear = Integer.MIN_VALUE;   
  15.         for (int i = 0; i < 100; i++) {   
  16.             int year = prisonBreak();   
  17.             if (minYear > year) {   
  18.                 minYear = year;   
  19.             }   
  20.             if (maxYear < year) {   
  21.                 maxYear = year;   
  22.             }   
  23.             System.out.print(prisonBreak() + ",");   
  24.         }   
  25.         System.out.println();   
  26.         System.out.println("MinYear:" + minYear);   
  27.         System.out.println("MaxYear:" + maxYear);   
  28.     }   
  29.   
  30.     private static int prisonBreak() {   
  31.         Random r = new Random();   
  32.         boolean lightOn = r.nextBoolean(); // 灯的初始状态   
  33.         int[] prisoner = new int[PRISONER_COUNT]; // 犯人,其值为开灯次数,最多两次   
  34.         final int COUNTER_ID = r.nextInt(PRISONER_COUNT); // 随机指定计数员,只负责关灯   
  35.         int counter = 0// 计数员记下的关灯次数   
  36.         int days = 0// 总共所花的天数   
  37.         int freeCount = (PRISONER_COUNT - 1) * 2// 关灯次数达到此数时就自由了   
  38.         while (counter < freeCount) {   
  39.             days++;   
  40.             int n = r.nextInt(PRISONER_COUNT); // 随机出来放风的犯人   
  41.             if (n == COUNTER_ID) {// 计数员   
  42.                 if (lightOn) { // 灯是开的就关灯   
  43.                     lightOn = false;   
  44.                     counter++;   
  45.                     prisoner[COUNTER_ID]++; // 计数员记的是关灯数   
  46.                 }   
  47.             } else { // 其他犯人   
  48.                 if (!lightOn && prisoner[n] < 2) { // 灯是关的,且开灯次数不超过2   
  49.                     lightOn = true;   
  50.                     prisoner[n]++;   
  51.                 }   
  52.             }   
  53.         }   
  54.         // 检查结果   
  55.         int sum = 0;   
  56.         for (int i = 0; i < PRISONER_COUNT; i++) {   
  57.             if (prisoner[i] > 0) { // 开过灯,则证明其放过风   
  58.                 sum++;   
  59.             }   
  60.         }   
  61.         assert sum == PRISONER_COUNT : "something wrong.";   
  62.         return days / 365// 大约   
  63.     }   

 

 

最佳逻辑方案,可以不考虑灯的初始状态:

很明确的条件:
1、每人一间牢房,都与外界隔绝,什么也听不见、看不到,连时间都没法计算
说明:没有人知道自己是第几天出来,所以不要再讨论说哪个囚犯是第一天出来就是记数员
2、每天会随机(注意是完全随机)打开一间牢房的门,让那个囚犯到院子里来放风。
说明:随机抽取,囚犯只能知道自己出来跟回去,以及看到灯的状态

我们在这里,只是为解题而解题,不需要考虑题目本身的问题,不用管花了多少天多少年,国王是否会死掉的情况。
如果条件是每10分钟随机放一个囚犯出来,那就快很多了,所以没必要去理会命题本身的合理性,只要考虑是否有
100%的不失误的方案,不带一丝侥幸。

解题方案:
1、指定99个一般囚犯,他们的有两个行为:一是把状态是开的灯关掉,否则不动;二是统计自己关掉灯的次数,如果有两次后,就再也不去开或者关这个灯。
2、指定1个记数员囚犯,他只有两个行为:一是把状态是关的灯打开,否则不动;二是负责统计从自己第一次(注意:不是第一天)出来开始算,(如果自己第一次出来灯是亮的,那么证明自己是第一个出来,同时也证明灯开始状态是亮的;如果自己第一次出来灯是暗的,那无法确认灯开始的状态以及自己是第几个出来)灯的被关次数达到198次,那么就可以确认所有的人都曾到院子里去过。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值