一、题目
100 prisoners are lined up and a black or white hat is to be placed on each of their heads. The prisoners cannot see the color of the hat on their own head, but they can see the colors of all the hats in front of them.
A guard is going to walk down the line, starting in the back, and ask each prisoner what color hat they have on. They can only answer “black” or “white.” If they answer incorrectly, or say anything else, they will be shot dead on the spot. If they answer correctly, they will be set free. Each prisoner can hear all of the other prisoners’ responses, as well as any gunshots that indicate an incorrect response. They can remember all of this information.
Before the executions begin, the prisoners get to huddle up and make a plan. How can the prisoners ensure that the most people possible survive?
一、中文题目:
100名囚犯被排成一列,每个人头上都要戴上一顶黑色或白色的帽子。囚犯们看不到自己头上帽子的颜色,但是他们可以看到他们面前所有帽子的颜色。
一个警卫从后面开始沿着队伍走,问每个囚犯戴着什么颜色的帽子。他们只能回答“黑”或“白”。如果他们回答错了,或者说了别的什么,他们就会被当场击毙。如果他们回答正确,他们将被释放。每个囚犯都能听到其他所有囚犯的反应,以及任何表明错误反应的枪声。他们可以记住所有这些信息。
在处决开始之前,囚犯们挤在一起制定计划。囚犯们怎样才能确保尽可能多的人存活下来?
二、解题思路
- 最后一个人如果看到奇数顶帽子报“黑”;否则报“白”,他可能死;
- 从倒数第二人开始,就有两个信息:记住的值与看到的值,相同报“白”,不同报“黑”。
此方案下,囚犯们的存活率是:99人能100%活,1人50%能活
三、用代码验证结论
1)先创建一个分配方案类,代码如下:
package math_puzzles;
public class solution {
public int[] indi;
public boolean[] survive;
public int[] getIndi() {
return indi;
}
public void setIndi(int[] indi) {
this.indi = indi;
}
public boolean[] getSurvive() {
return survive;
}
public void setSurvive(boolean[] survive) {
this.survive = survive;
}
public solution() {
// TODO Auto-generated constructor stub
}
}
2)做100次实验验证提出的方案能否达到以下目标:99人能100%活,1人50%能活
package math_puzzles;
import java.lang.Math; //导入Random类bai
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import math_puzzles.solution;
public class prisoner_problem {
public static final int LEN = 100;
/*随机初始化一个序列,0与1的概率都为0.5
* int[] indi : 代表囚徒帽子颜色序列
* */
public static void generateChrome(int[] indi) {
for(int i = 0;i < LEN;i++)
//此处的0.5是个参数,可以调参;随机数生成方式可以采用动态分布等生成,此处为了方便采用了java中Math提供的随机数生成方式
if(Math.random() > 0.5)
indi[i] = 1;
}
/*统计end之前,黑帽子的数量
* int[] indi : 100个囚徒所戴帽子颜色序列
* int end : 要统计end之前黑色帽子的量
* */
public static int getBlackHatNum(int[] indi,int end) {
//记录下1的数目
int num = 0;
for(int i = 0;i < end;i++)
num += indi[i];
return end - num;
}
/* 初始化解:
* solution s : 要生成的解
* 为了确保尽可能多的人存活下来,囚犯们制定的计划:
* 1、 最后一名囚犯计算前面所有黑帽子的个数,如果是奇数个,就猜黑,如果是偶数个,就猜白。
2、其他人记住这个值(实际是黑帽奇偶数),在此之后当再听到黑时,取反一次
3、从倒数第二人开始,就有两个信息:记住的值与看到的值,相同报“白”,不同报“黑”
99人能100%活,1人50%能活
*
* */
public static void run(solution s) {
boolean survive[] = new boolean[LEN];
// TODO Auto-generated method stub
/*1.生成一个大小为100,内容为1或0的数组,
1 : 白帽;
0 : 黑帽。
*/
int indi[] = new int[LEN];
int res[] = new int[LEN];
generateChrome(indi);
s.setIndi(indi);
boolean flag = true; //看到的值标记,奇数为 true,偶数为 false
//2. 最后一名囚犯计算前面所有黑帽子的个数,如果是奇数个,就猜黑,如果是偶数个,就猜白。
int tmp = LEN - 1;
flag = getBlackHatNum(indi,tmp) % 2 == 1 ? true : false;
if(flag) //如果是奇数个,就猜黑,如果是偶数个,就猜白。
res[tmp] = 0;
else
res[tmp] = 1;
survive[tmp] = indi[tmp] != res[tmp] ? false : true;
//3.从倒数第二人开始,就有两个信息:记住的值与看到的值,相同报“白”,不同报“黑”
boolean preFlag = flag; //记住的值标记
for(int i = LEN - 2;i >= 0;i--) {
flag = getBlackHatNum(indi,i) % 2 == 1 ? true : false;
if(flag == preFlag)
res[i] = 1; //记住的值与看到的值,相同报“白”
else
res[i] = 0; //不同报“黑”
preFlag = flag;
}
//survive数组记录下哪个囚徒会死
for(int i = 0;i < LEN - 1;i++)
if(indi[i] != res[i])
survive[i] = false;
else
survive[i] = true;
s.setSurvive(survive);
}
/*验证run_time(此处为:100)次实验中,囚徒的生还率是不是:99人能100%活,1人50%能活
* */
public static void main(String[] args) {
int run_time = 100;
ArrayList<solution> list = new ArrayList<solution>();
for(int i = 0;i < run_time;i++) {
solution s = new solution();
run(s);
list.add(s);
}
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for(int i = 0;i < LEN;i++) {
int count = 0;
for(int j = 0;j < run_time;j++) {
if(list.get(j).survive[i])
count++;
}
if(map.containsKey(count)){
int value = map.get(count);
value = value + 1;
map.put(count, value);
}else{
map.put(count, 1);
}
}
for(Map.Entry<Integer, Integer> entry : map.entrySet()){
int key = entry.getKey();
int value = entry.getValue();
System.out.println("此方案下,囚犯们的存活率是:" + value + "人能" + key + "%活");
}
}
}
四、结论
针对此问题,提出的方案,能够实现“99人能100%活,1人50%能活”的目标。所以,提出的算法是真实的、有效的。