您的位置:首页 > 编程语言 > C语言/C++

用程序解密爱因斯坦经典难题(C++)

2016-07-15 15:21 417 查看
爱因斯坦曾在20世纪初提过一个经典问题,据说世界上有98%的人回答不出来

问题:在一条街上,有5座房子,喷了5中颜色。每个房子住着不同国籍的人。每个人喝不同的饮料,抽不同品牌的香烟,养不同的宠物。

问题是:谁养鱼?

提示:1.英国人住红色房子

2.瑞典人养狗

3.丹麦人喝茶

4.绿色房子在白色房子左边

5.绿房子主人喝咖啡

6.抽PallMall香烟的人养鸟

7.黄色房子的主人抽Dunhill香烟

8.住在中间房子的人喝牛奶

9.挪威人住第一间房

10.抽Bleeds香烟的人住在养猫的人隔壁

11.养马的人住抽Dunhill香烟的人隔壁

12.抽BlueMaster的人喝啤酒

13.德国人抽Prince香烟

14.挪威人住蓝色房子隔壁

15.抽Bleeds香烟的人有一个喝水的邻居

这个版本的程序我将不会使用任何推理,仅仅用程序描述这15个条件,然后穷举所有组合得到满足这15个条件的唯一解答。

首先构造“人”这个类型,根据题目可以知道每个人拥有颜色,香烟,饮料,宠物,位置,国籍这6个属性。虽然在现实中,人抽香烟,喝饮料,养宠物,拥有国籍,只有这4个属性,颜色和位置是房子的属性,但是人住在哪间房子,就决定了这个人的颜色和位置,所以简便起见,这6种属性全部归类到一个抽象的“人”中。新建一个Person.h的文件:

class Person
{
public:
Person(Color,Country ,Drink ,Cigarette ,Pet ,int);
~Person(void);
Color color;
Country country;
Drink drink;
Cigarette cigarette;
Pet pet;
int index;
};

根据这6种属性,可以创造出5^6个不同的“人”。接着,观察这15个条件,条件1,2,3,5,6,7,8,9,12,13,14,全部是以单个人作为约束条件,而剩下的4个都是以2个人之间的关系作为约束条件。首先可以把不满足条件的人过滤掉,再通过组合判断剩下的条件是否满足。比如1.英国人住红色房子,那么国籍是英国但是颜色不是红色的人就是不满足条件的,反过来,颜色是红色但是国籍不是英国的人也不满足条件,用程序的描述就是:

bool test1(const Person &p)
{
if((p.country==England&&p.color!=Red)||(p.country!=England&&p.color==Red))
return false;
return true;
}


同理剩下的以人为约束的条件描述为:

bool test2(const Person &p)
{
if((p.country==Sweden&&p.pet!=Dog)||(p.country!=Sweden&&p.pet==Dog))
return false;
return true;
}

bool test3(const Person &p)
{
if((p.country==Denmark&&p.drink!=Tea)||(p.country!=Denmark&&p.drink==Tea))
return false;
return true;
}

bool test5(const Person &p)
{
if((p.color==Green&&p.drink!=Coffee)||(p.color!=Green&&p.drink==Coffee))
return false;
return true;
}

bool test6(const Person &p)
{
if((p.cigarette==PallMall&&p.pet!=Bird)||(p.cigarette!=PallMall&&p.pet==Bird))
return false;
return true;
}

bool test7(const Person &p)
{
if((p.color==Yellow&&p.cigarette!=Dunhill)||(p.color!=Yellow&&p.cigarette==Dunhill))
return false;
return true;
}

bool test8(const Person &p)
{
if((p.index==3&&p.drink!=Milk)||(p.index!=3&&p.drink==Milk))
return false;
return true;
}

bool test9(const Person &p)
{
if((p.country==Norway&&p.index!=1)||(p.country!=Norway&&p.index==1))
return false;
return true;
}

bool test12(const Person &p)
{
if((p.cigarette==BlueMaster&&p.drink!=Bear)||(p.cigarette!=BlueMaster&&p.drink==Bear))
return false;
return true;
}

bool test13(const Person &p)
{
if((p.country==Germany&&p.cigarette!=Prince)||(p.country!=Germany&&p.cigarette==Prince))
return false;
return true;
}
//条件14可直接表示为蓝色房子是第二间房
bool test14(const Person &p)
{
if((p.color==Blue&&p.index!=2)||(p.color!=Blue&&p.index==2))
return false;
return true;
}
筛选出了有效的“人”,接着把这些“人”装进不同的"房子"里

vector<Person> v1;
vector<Person> v2;
vector<Person> v3;
vector<Person> v4;
vector<Person> v5;
for(int color=0;color<5;++color)
for(int country=0;country<5;++country)
for(int drink=0;drink<5;++drink)
for(int cigarette=0;cigarette<5;++cigarette)
for(int pet=0;pet<5;++pet)
for(int index=1;index<=5;++index)
{
Person p((Color)color,(Country)country,(Drink)drink,(Cigarette)cigarette,(Pet)pet,index);
if(test1(p)&&test2(p)&&test3(p)&&test5(p)&&test6(p)&&test7(p)&&test8(p)&&test9(p)&&test12(p)&&test13(p)&&test14(p))
{
switch(p.index)
{
case 1:
v1.push_back(p);
break;
case 2:
v2.push_back(p);
break;
case 3:
v3.push_back(p);
break;
case 4:
v4.push_back(p);
break;
case 5:
v5.push_back(p);
break;
}
}
}


5个集合分别表示编号为1-5的5个房子,通过循环构建“人”,把符合条件的人扔到不同的房子里去。这样一来,得到了5个房子,每个房子里都有一些人,这些人都是已经满足了前面所有条件的,接着每个房子里分别派出一个人,判断这5个人是否能满足剩下的条件,如果满足,那这5个人就是答案,不满足,那就换人,直到所有的人都组合过就结束。

size_t s1=v1.size();
size_t s2=v2.size();
size_t s3=v3.size();
size_t s4=v4.size();
size_t s5=v5.size();
int count=0;
string countryName[5]={"英国人","瑞典人","丹麦人","挪威人","德国人"};
string colorName[5]={"蓝色","绿色","白色","红色","黄色"};
string drinkName[5]={"茶","牛奶","咖啡","啤酒","水"};
string cigaretteName[5]={"PallMall","Dunhill","Bleeds","BlueMaster","Prince"};
string petName[5]={"鱼","狗","鸟","猫","马"};
for(int i1=0;i1<s1;++i1)
for(int i2=0;i2<s2;++i2)
for(int i3=0;i3<s3;++i3)
for(int i4=0;i4<s4;++i4)
for(int i5=0;i5<s5;++i5)
{
Person pp[5]={v1[i1],v2[i2],v3[i3],v4[i4],v5[i5]};
if(testDistinct(pp)&&test4(pp)&&test10(pp)&&test11(pp)&&test15(pp))
{
cout<<"答案"<<++count<<":"<<endl;
for(int i=0;i<5;++i)
{
cout<<"第"<<i+1<<"间房是"<<colorName[pp[i].color]<<",住的是"<<countryName[pp[i].country]<<",喝"<<drinkName[pp[i].drink]<<",抽"<<cigaretteName[pp[i].cigarette]<<",养"<<petName[pp[i].pet]<<endl;
}
}
}


这里除了剩下的4个条件还有一个隐藏条件,那就是人的属性是不能重复的,例如这5个人当中不能有2个英国人,也不能有2个人或者3个人同时养鱼。

去重的验证,这里用了一点小技巧:

bool testDistinct(Person *pp)
{
int state[25]={0};
for(int i=0;i<5;++i)
{
for(int j=0;j<5;++j)
{
state[j]+=pp[i].color==(Color)j;
state[j+5]+=pp[i].drink==(Drink)j;
state[j+10]+=pp[i].cigarette==(Cigarette)j;
state[j+15]+=pp[i].pet==(Pet)j;
state[j+20]+=pp[i].country==(Country)j;
}
}

for(int i=0;i<25;i++)
{
if(state[i]!=1)
return false;
}
return true;
}
人已经被分到不同房子里了,所以这5个人的index肯定是不会重复了,只需判断剩下的5个属性是否重复。每种属性都是5个状态,5种属性就是25个状态,然后用一个长度为25的一维数组来表示,比如state[0]表示蓝色属性的数量,state[1]表示绿色属性的数量,state[5]表示喝茶人的数量,然后5个人循环判断是否存在这些状态,如果有那么state数组计数就加1。如果组合是不存在重复的,也就表示state数组的每一项都正好是1,如果其中某一项不等于1,就表示一定存在重复。

剩下的4个条件判断:

bool test4(Person *pp)
{
Person *p1,*p2;
for(int i=0;i<5;++i)
{
if(pp[i].color==Green)
p1=&pp[i];
if(pp[i].color==White)
p2=&pp[i];
}
return p1->index==p2->index-1;
}

bool test10(Person *pp)
{
Person *p1,*p2;
for(int i=0;i<5;++i)
{
if(pp[i].cigarette==Bleeds)
p1=&pp[i];
if(pp[i].pet==Cat)
p2=&pp[i];
}
return p1->index==p2->index-1||p1->index==p2->index+1;
}

bool test11(Person *pp)
{
Person *p1,*p2;
for(int i=0;i<5;++i)
{
if(pp[i].pet==Horse)
p1=&pp[i];
if(pp[i].cigarette==Dunhill)
p2=&pp[i];
}
return p1->index==p2->index-1||p1->index==p2->index+1;
}

bool test15(Person *pp)
{
Person *p1,*p2;
for(int i=0;i<5;++i)
{
if(pp[i].drink==Water)
p1=&pp[i];
if(pp[i].cigarette==Bleeds)
p2=&pp[i];
}
return p1->index==p2->index-1||p1->index==p2->index+1;
}


原理就是从5个人中找到条件中的2个人,然后判断他们的index属性,所谓邻居,隔壁都是表示他们的index是相连的。

到这里代码就已经写完了,最后的计算结果是:



最后总结:为什么有的问题人无法解决却可以用计算机解决,虽然这个题目用画表格的方法推理出答案也不太难,但是相比较而言,计算机程序的思路更简单粗暴,而人不可能用这种方式思考问题。假如你要计算987*512,而你并不会乘法口诀,你怎么得到结果?这时给了你一张无比巨大的草稿纸,而且在上面的书写速度会非常快,幸好你的加法还不错,于是你不断演算987+987=1974,1974+987=2961...至到加上512个987为止,最终你得到了正确答案,由于书写速度非常快,甚至快过你用乘法口诀得出答案,这就是计算机解决人无法解决问题的原理,计算机就好比这样的草稿纸。至于如何把问题描述给计算机计算(把乘法问题转成加法问题),以及让计算机运行得更快(你可以累加512次,你也可以仅仅累加9次),这就涉及到计算机科学的一门重要课程--数据结构与算法。

本文章原创地址:/article/11914321.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: