爱因斯坦的思考题-csdn博客
这个问题是我偶然间在博客首页的一个推销课程的博客中知道的这个问题。看完后发现思考着很有趣。于是我思考并学习后在这里给大家分享一下,并且从我们人的角度和计算机求解的角度探讨一下求答案的过程。
- 题目如下:
据说有五个不同颜色的房间排成一排,每个房间里分别住着一个不同国籍的人,每个人都喝一种特定品牌的饮料,抽一种特定品牌的烟,养一种宠物,没有任意两个人抽相同品牌的香烟,或喝相同品牌的饮料,或养相同的宠物。
问题是谁在养鱼作为宠物?为了寻找答案,爱因斯坦给出了以下 15 条线索。
- 英国人住在红色的房子里;
- 瑞典人养狗作为宠物;
- 丹麦人喝茶;
- 绿房子紧挨着白房子,在白房子的左边;
- 绿房子的主人喝咖啡;
- 抽 Pall Mall 牌香烟的人养鸟;
- 黄色房子里的人抽 Dunhill 牌香烟;
- 住在中间那个房子里的人喝牛奶;
- 挪威人住在第一个房子里面;
- 抽 Blends 牌香烟的人和养猫的人相邻;
- 养马的人和抽 Dunhill 牌香烟的人相邻;
- 抽 BlueMaster 牌香烟的人喝啤酒;
- 德国人抽 Prince 牌香烟;
- 挪威人和住在蓝房子的人相邻;
- 抽 Blends 牌香烟的人和喝矿泉水的人相邻。
人的思维:
在这里我首先要说的是每个人都不一样,思考方式也不一样。解题过程也很多,只需要一遍又一遍对比条件找突破口就行。因此,如果自己已经思考处了答案,那么只需对比一下答案就行了,我的思路不用看也没问题的O(∩_∩)O哈哈~
为了简化,用语法 tx:指的是条件x推出结论
t1: 红色-英国人
t8: 中间房子-和牛奶
t9:挪威人住第一个房子
t9&t14:第二个房子为蓝色
t4&t5:第四个房子为绿色&和咖啡,第五个房子为白色
此时还剩下红色和黄色房子,因为红色为英国人,因此,挪威人为黄色房子
做出的表格如下
t5:绿色-喝咖啡
t7:黄色-抽Dunhill
t11:蓝色-养马
此时根据喝的饮料在做一步推论:t2:丹麦人-喝茶发现剩下剩余啤酒和矿泉水,根据t12发现挪威人和矿泉水
此时表格如下:
t15:蓝色-抽Blender
t12:白色-抽BlueMaster-喝啤酒
t3:蓝色-喝茶-丹麦人
t13:绿色-德国人-抽Prince
然后白色的就是瑞典人了
此时表格如下:
那么此时红色也就抽Pall Mall了
t6:红色-抽Pall Mall-养鸟
t10:黄色-养猫
t2:白色-养狗
那么此时就的除了最终的表格
此时,很现任答案已经清楚了,德国人养鱼
计算机角度分析
有于计算机并不会这样推理,只能经过公式递推或者枚举判断的方法。因此,计算机的角度很现然就是想法枚举所有的可能并进行判断来找出答案。那么问题便转换为了如何把所有的情况都进行枚举以及如何判断了。
首先对属性划分:共有5个属性,每种属性共有5个值。属性分别是房子颜色、人、喝的饮料、抽的烟、养的动物。此时每种属性的全排列共有5!种可能,那么总共要枚举的可能有5!^5 >1e10,很显然是不可能用这种复杂度些程序的。可是框架已经是枚举了,此时只能用判断条件来减少枚举的时间复杂度了。第一种方法是通过条件中的一些,对已经很明确固定的位置,进行确认,来减少枚举的次数,并在最后进行一次全部的判断是否成立。第二种方法是在每层属性的枚举时,就要对在这个层次已经可以进行的判断的条件进行判断,如果不匹配,那么直接下面的就不用枚举了(即枚举处便判断是非否成立)。很闲然,第二种方法效率更高
第一种方法(随意确定的,答案不固定,只要最后在计算机计算能力之内便可):
想了想_,还是不做说明了。还是要推论的。
第二种方法:首先自己要对自己枚举属性进行划分,即那个属性在那一层,我便按照房子颜色、人、喝的饮料、抽的烟、养的动物了。然后对条件进行划分,然后添加到相应枚举层次中判断即可。
例如:
条件9: 加在第二层用来判断第一位是否是个挪威人,保证符合条件。
条件1: 当在第二层枚举到英国人的时候便判断,上一层这个位置是否是红色。
条件4: 当在第一层到白色房子是判断前一个位置是否为绿色。
等等。如此判断。
- 贴个代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
//store answer;
string sn[5][5];
string s1[5]={"白色","蓝色","红色","绿色","黄色"};
string s2[5]={"瑞典","丹麦","英国","德国","挪威"};
string s3[5]={"啤酒","茶","牛奶","咖啡","矿泉水"};
string s4[5]={"BlueMaster","Blender","Pall Mall","Prince","Dunhill"};
string s5[5]={"狗","马","鸟","鱼","猫"};
bool fun4(int *m){
for(int i=0;i<4;i++){
if(s1[m[i]]=="绿色"&&s1[m[i+1]]=="白色")return true;
}
return false;
}
bool fun9(int *m){
if(s2[m[0]]=="挪威")return true;
else return false;
}
bool fun14(int *m){
if(s1[m[1]]=="蓝色")return true;
else return false;
}
bool fun3(int *m1,int *m2){
for(int i=0;i<5;i++)
if(s2[m1[i]]=="丹麦"&&s3[m2[i]]=="茶")return true;
return false;
}
bool fun5(int *m1,int *m2){
for(int i=0;i<5;i++)
if(s1[m1[i]]=="绿色"&&s3[m2[i]]=="咖啡")return true;
return false;
}
bool fun8(int *m){
if(s3[m[2]]=="牛奶")return true;
return false;
}
bool fun7(int *m1,int *m2){
for(int i=0;i<5;i++)
if(s1[m1[i]]=="黄色"&&s4[m2[i]]=="Dunhill")return true;
return false;
}
bool fun12(int *m1,int *m2){
for(int i=0;i<5;i++)
if(s3[m1[i]]=="啤酒"&&s4[m2[i]]=="BlueMaster")return true;
return false;
}
bool fun13(int *m1,int *m2){
for(int i=0;i<5;i++)
if(s2[m1[i]]=="德国"&&s4[m2[i]]=="Prince")return true;
return false;
}
bool fun2(int *m1,int *m2){
for(int i=0;i<5;i++)
if(s2[m1[i]]=="瑞典"&&s5[m2[i]]=="狗")return true;
return false;
}
bool fun6(int *m1,int *m2){
for(int i=0;i<5;i++)
if(s4[m1[i]]=="Pall Mall"&&s5[m2[i]]=="鸟")return true;
return false;
}
bool fun10(int *m1,int *m2){
for(int i=0;i<5;i++)
if(s4[m1[i]]=="Blender"){
if(i>0&&s5[m2[i-1]]=="猫")return true;
if(i<4&&s5[m2[i+1]]=="猫")return true;
}
return false;
}
bool fun11(int *m1,int *m2){
for(int i=0;i<5;i++)
if(s4[m1[i]]=="Dunhill"){
if(i>0&&s5[m2[i-1]]=="马")return true;
if(i<4&&s5[m2[i+1]]=="马")return true;
}
return false;
}
bool fun15(int *m1,int *m2){
for(int i=0;i<5;i++)
if(s4[m1[i]]=="Blender"){
if(i>0&&s3[m2[i-1]]=="矿泉水")return true;
if(i<4&&s3[m2[i+1]]=="矿泉水")return true;
}
return false;
}
bool fun1(int *m1,int *m2){
for(int i=0;i<5;i++)
if (s1[m1[i]]=="红色"&&s2[m2[i]]=="英国")return true;
return false;
}
int main()
{
int a[5],b[5],c[5],d[5],e[5];//5个属性进行枚举
for(int i=0;i<5;i++)a[i]=i;
bool f=true;//判断是否找到
do{//first level
if(!fun4(a))continue;
for(int i=0;i<5;i++)b[i]=i;
do{//second level
if(!fun1(a,b))continue;
if(!fun9(b))continue;
if(!fun14(a))continue;
for(int i=0;i<5;i++)c[i]=i;
do{//third level
if(!fun3(b,c))continue;
if(!fun5(a,c))continue;
if(!fun8(c))continue;
for(int i=0;i<5;i++)d[i]=i;
do{//forth level
if(!fun7(b,d))continue;
if(!fun12(c,d))continue;
if(!fun13(b,d))continue;
if(!fun15(d,c))continue;
for(int i=0;i<5;i++)e[i]=i;
do{//fifth level
if(!fun2(b,e))continue;
if(!fun6(d,e))continue;
if(!fun10(d,e))continue;
if(!fun11(d,e))continue;
// cout<<"===="<<endl;
// for(int i=0;i<5;i++)cout<<s1[a[i]]<<" ";
// cout<<endl;
f=false;//find successful
}while(f&&next_permutation(e,e+5));
}while(f&&next_permutation(d,d+5));
}while(f&&next_permutation(c,c+5));
}while(f&&next_permutation(b,b+5));
}while(f&&next_permutation(a,a+5));
//把答案存到数组sn中
for(int i=0;i<5;i++)
{
sn[0][i]=s1[a[i]];
sn[1][i]=s2[b[i]];
sn[2][i]=s3[c[i]];
sn[3][i]=s4[d[i]];
sn[4][i]=s5[e[i]];
}
//输出答案
for(int i=0;i<5;i++)
{
for(int j=0;j<5;j++)
cout<<sn[i][j]<<" ";
cout<<endl;
}
if(f)cout<<"21"<<endl;
else cout<<"123"<<endl;
return 0;
}
好了,欢迎大家观看我的分享,如果大家有什么别的好方法或者纠正或者建议,欢迎大家给我留言O(∩_∩)O哈哈~