題目:
有五座房子,每座房子的顏色不同,裏面分別住着不同國家的人,每個人都有自己養的不同寵物喜歡喝的不同飲料、抽的不同牌子的煙。現已知以下一些信息:英國人住在紅色的房子裏 ;
西班牙人養了一條狗;
挪威人住在左邊的第一個房子裏;
黃房子裏的人喜歡抽kools牌的香菸;
抽chesterfields牌香菸的人與養狐狸的人是鄰居;
挪威人住在藍色房子旁邊;
抽winston牌香菸的人養了一個蝸牛;
抽lucky strike牌香菸的人喜歡喝橘子汁;
烏克蘭人喜歡喝茶;
日本人抽parlianments牌的煙;
抽kools牌香菸的人與養馬的人是鄰居;
喜歡喝咖啡的人住在綠房子裏;
綠房子在象牙白房子的右邊;
中間那個房子裏的人喜歡喝牛奶。
根據以上條件,請你判斷哪個房子裏的人養斑馬?哪個房子裏的人喜歡喝水?最後把所有的東西對號入座。
一開始想法很簡單,直接暴力破解,於是有了這個算法的雛形,不過稍微計算一下後發現,244883200000種組合跑起來怕不是一時半會能出結果的……於是不停改進算法,最後終於發現一個真理:一條判斷勝過千萬次的運算(多麼痛苦的領悟!),這個程序告訴我們決策的重要性!
程序運行結果如下:
先上代碼:
//有五座房子,每座房子的顏色不同,裏面分別住着不同國家的人,每個人都有自己養的不同寵物、
//喜歡喝的不同飲料、抽的不同牌子的煙。現已知以下一些信息:
//英國人住在紅色的房子裏;
//西班牙人養了一條狗;*
//挪威人住在左邊的第一個房子裏;
//黃房子裏的人喜歡抽kools牌的香菸;*
//抽chesterfields牌香菸的人與養狐狸的人是鄰居;*
//挪威人住在藍色房子旁邊;
//抽winston牌香菸的人養了一個蝸牛;*
//抽lucky strike牌香菸的人喜歡喝橘子汁;*
//烏克蘭人喜歡喝茶;*
//日本人抽parlianments牌的煙;*
//抽kools牌香菸的人與養馬的人是鄰居;*
//喜歡喝咖啡的人住在綠房子裏;
//綠房子在象牙白房子的右邊;
//中間那個房子裏的人喜歡喝牛奶。
//根據以上條件,請你判斷哪個房子裏的人養斑馬?哪個房子裏的人喜歡喝水?最後把所有的東西對號入座。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
namespace 愛因斯坦智力題暴力破解
{
class Program
{
static DateTime t1=new DateTime();
static DateTime t2 = new DateTime();
static void Main(string[] args)
{
//被計時的代碼段
t1 = DateTime.Now;
Start s = new Start();
Start.h1();
t2 = DateTime.Now;
//
Console.WriteLine("\n耗時: {0} 秒", (t2 - t1).TotalSeconds);
Console.ReadKey(true);
}
}
public class Start
{
static Person[] persons;
static int contains = 0;
static bool[] Isbuild;
public Start()
{
persons = new Person[7];//實際使用1~5,0和6用來作相鄰判斷時防止數組越界
persons[0] = new Person();
persons[6] = new Person();
Isbuild = new bool[5] { false, false, false, false, false };
}
//組裝第一間房子(左數第一間)
public static bool h1()
{
for (int h = 1; h <= 5; h++)
for (int p = 1; p <= 5; p++)
for (int d = 1; d <= 5; d++)
for (int s = 1; s <= 5; s++)
{
persons[1] = new Person(h, 3, p, d, s);//挪威人住在左邊的第一個房子裏;
if (!Isbuild[0])
{
Isbuild[0] = true;
contains++;
}
//第一間房子不是紅色、藍色、白色,也不喝牛奶,茶,不抽parlianments,不養狗(額,後面幾個都註釋後才發現類似下面的判斷加不加都無所謂,速度上只是0.1s的差距)
//if (persons[1] .housecolor != HouseColor.紅色 && persons[1] .housecolor != HouseColor.藍色 && persons[1] .housecolor != HouseColor.白色 && persons[1] .drink != Drink.牛奶 && persons[1] .drink != Drink.茶&&persons[1] .pet!=Pet.狗&&persons[1] .smoke!=Smoke.parlianments)
if (term())
{
if (!h2())
continue;
}
}
return false;
}
//組裝第二間房子
public static bool h2()
{
for (int c = 1; c <= 5; c++)
for (int p = 1; p <= 5; p++)
for (int d = 1; d <= 5; d++)
for (int s = 1; s <= 5; s++)
{
persons[2] = new Person(5, c, p, d, s);//第二間房子是藍色的
if (!Isbuild[1])
{
Isbuild[1] = true;
contains++;
}
if (persons[2] .Equals(persons[1] ))
continue;
//第二間房子不住挪威人、英國人,不喝咖啡、牛奶,不抽kools的煙
//if (persons[2] .country != Country.挪威 && persons[2] .country != Country.英國 && persons[2] .drink != Drink.咖啡 && persons[2] .drink != Drink.牛奶 && persons[2] .smoke != Smoke.kools)
if (term())
{
if (!h3())
continue;
}
}
persons[2] = new Person(0, 0, 0, 0, 0);
Isbuild[1] = false;
contains--;
return false;
}
//組裝第三間房子
public static bool h3()
{
for (int h = 1; h <= 5; h++)
for (int c = 1; c <= 5; c++)
for (int p = 1; p <= 5; p++)
for (int s = 1; s <= 5; s++)
{
persons[3] = new Person(h, c, p, 2, s);//中間房子的人喝牛奶
if (!Isbuild[2])
{
Isbuild[2] = true;
contains++;
}
if (persons[3] .Equals(persons[2] )|| persons[3] .Equals(persons[1] ))
continue;
//第三間房自不是綠色、藍色,不住烏克蘭人,不住挪威人,不抽lucky strike的煙
//if (persons[3] .housecolor != HouseColor.綠色 && persons[3] .housecolor != HouseColor.藍色 && persons[3] .country != Country.烏克蘭 && persons[3] .country != Country.挪威&& persons[3] .smoke != Smoke.lucky_strike)
if (term())
{
if (!h4())
continue;
}
}
persons[3] = new Person(0, 0, 0, 0, 0);
Isbuild[2] = false;
contains--;
return false;
}
//組裝第四間房子
public static bool h4()
{
for (int h = 1; h <= 5; h++)
for (int c = 1; c <= 5; c++)
for (int p = 1; p <= 5; p++)
for (int d = 1; d <= 5; d++)
for (int s = 1; s <= 5; s++)
{
persons[4] = new Person(h, c, p, d, s);//第四間房子
if (!Isbuild[3])
{
Isbuild[3] = true;
contains++;
}
if (persons[4] .Equals(persons[3] ) || persons[4] .Equals(persons[2] ) || persons[4] .Equals(persons[1] ))
continue;
//第四間房子不是藍色、紅色、黃色,不住挪威人,不喝牛奶
//if (persons[4] .housecolor != HouseColor.藍色 && persons[4] .housecolor != HouseColor.紅色 && persons[4] .housecolor != HouseColor.黃色 && persons[4] .country != Country.挪威 && persons[4] .drink != Drink.牛奶)
if (term())
{
if (!h5())
continue;
}
}
persons[4] = new Person(0, 0, 0, 0, 0);
Isbuild[3] = false;
contains--;
return false;
}
//組裝第五間房子
public static bool h5()
{
for (int h = 1; h <= 5; h++)
for (int c = 1; c <= 5; c++)
for (int p = 1; p <= 5; p++)
for (int d = 1; d <= 5; d++)
for (int s = 1; s <= 5; s++)
{
persons[5] = new Person(h, c, p, d, s);//第五間房子
if (!Isbuild[4])
{
Isbuild[4] = true;
contains++;
}
if (persons[5].Equals(persons[4] ) || persons[5].Equals(persons[3] ) || persons[5].Equals(persons[2] ) || persons[5].Equals(persons[1] ))
continue;
//第五間房子不是藍色、白色,不住挪威人,不喝牛奶
//if (persons[5].housecolor != HouseColor.藍色 && persons[5].housecolor != HouseColor.白色 && persons[5].country != Country.挪威 && persons[5].drink != Drink.牛奶)
test();
}
persons[5] = new Person(0,0,0,0,0);
Isbuild[4] = false;
contains--;
return false;
}
public static bool term()
{
for (int i = 1; i <= contains ; i++)
if (persons[i].country == Country.英國 && persons[i].housecolor != HouseColor.紅色 || persons[i].country != Country.英國 && persons[i].housecolor == HouseColor.紅色)
return false;
for (int i =1; i <=contains; i++)
if (persons[i].country == Country.烏克蘭 && persons[i].drink != Drink.茶||persons[i].country != Country.烏克蘭 && persons[i].drink == Drink.茶)
return false;
for (int j =1; j <=contains ; j++)
if (persons[j].country == Country.西班牙 && persons[j].pet != Pet.狗 || persons[j].country != Country.西班牙 && persons[j].pet == Pet.狗)
return false;
for (int k = 1; k <=contains ; k++)
if (persons[k].housecolor == HouseColor.黃色 && persons[k].smoke != Smoke.kools || persons[k].housecolor != HouseColor.黃色 && persons[k].smoke == Smoke.kools)
return false;
for (int x = 1; x <=contains ; x++)
if (persons[x].pet == Pet.蝸牛 && persons[x].smoke != Smoke.winston || persons[x].pet != Pet.蝸牛 && persons[x].smoke == Smoke.winston)
return false;
for (int y = 1; y <=contains ; y++)
if (persons[y].drink == Drink.橘子汁 && persons[y].smoke != Smoke.lucky_strike || persons[y].drink != Drink.橘子汁 && persons[y].smoke == Smoke.lucky_strike)
return false;
for (int z = 1; z <=contains; z++)
if (persons[z].country == Country.日本 && persons[z].smoke != Smoke.parlianments || persons[z].country != Country.日本 && persons[z].smoke == Smoke.parlianments)
return false;
for (int l = 1; l <=contains; l++)
if (persons[l].housecolor == HouseColor.綠色 && persons[l].drink != Drink.咖啡 || persons[l].housecolor != HouseColor.綠色 && persons[l].drink == Drink.咖啡)
return false;
return true;
}
public static void test()
{
if(test_1()&&test_2()&&test_3())
{
ShowResult();
Anwser();
}
}
public static bool test_1()//已給定的二維條件4/8
{
for (int m = 1; m <= contains ; m++)
if (persons[m].country == Country.英國 && persons[m].housecolor == HouseColor.紅色)//英國人住在紅色的房子裏;
for (int i = 1; i <= contains ; i++)
if (persons[i].country == Country.烏克蘭 && persons[i].drink == Drink.茶)//烏克蘭人喜歡喝茶;
for (int j = 1; j <= contains ; j++)
if (persons[j].country == Country.西班牙 && persons[j].pet == Pet.狗)//西班牙人養了一條狗;
for (int k = 1; k <= contains ; k++)
if (persons[k].housecolor == HouseColor.黃色 && persons[k].smoke == Smoke.kools)//黃房子裏的人喜歡抽kools牌的香菸;
return true;
return false;
}
public static bool test_2()//已給定的二維條件4/8
{
for (int x = 1; x <= contains ; x++)
if (persons[x].pet == Pet.蝸牛 && persons[x].smoke == Smoke.winston)//抽winston牌香菸的人養了一個蝸牛;
for (int y = 1; y <= contains ; y++)
if (persons[y].drink == Drink.橘子汁 && persons[y].smoke == Smoke.lucky_strike)//抽lucky strike牌香菸的人喜歡喝橘子汁;
for (int z = 1; z <= contains ; z++)
if (persons[z].country == Country.日本 && persons[z].smoke == Smoke.parlianments)//日本人抽parlianments牌的煙;
for (int l = 1; l <= contains ; l++)
if (persons[l].housecolor == HouseColor.綠色 && persons[l].drink == Drink.咖啡)//喜歡喝咖啡的人住在綠房子裏;
return true;
return false;
}
public static bool test_3()//相鄰位置判斷條件3/3
{
for (int k = 1; k <= contains; k++)
if (persons[k].housecolor == HouseColor.白色 && persons[(k + 1) % 6].housecolor == HouseColor.綠色)//綠房子在象牙白房子的右邊;
for (int i = 1; i <= contains; i++)
if (persons[i].pet == Pet.狐狸 && persons[i - 1].smoke == Smoke.chesterfields || persons[i].pet == Pet.狐狸 && persons[(i + 1) % 6].smoke == Smoke.chesterfields)//抽chesterfields牌香菸的人與養狐狸的人是鄰居;
for (int j = 1; j <= contains; j++)
if (persons[j].smoke == Smoke.kools && persons[j - 1].pet == Pet.馬 || persons[j].smoke == Smoke.kools && persons[(j + 1) % 6].pet == Pet.馬)//抽kools牌香菸的人與養馬的人是鄰居;
return true;
return false;
}
public static void ShowResult()
{
for (int n = 1; n <= 5; n++)
Console.WriteLine(persons[n]);
Console.WriteLine("---------------------------------");
}
public static void Anwser()
{
Console.WriteLine("答案是:");
for (int i = 1; i <= 5; i++)
if (persons[i].drink == Drink.水)
Console.WriteLine(" {0}人喝{1}", persons[i].country, persons[i].drink);
for (int i = 1; i <= 5; i++)
if (persons[i].pet == Pet.斑馬)
Console.WriteLine(" {0}人養{1}",persons[i].country,persons[i].pet);
}
}
public enum HouseColor { 紅色 = 1, 白色, 黃色, 綠色, 藍色 }
public enum Country { 英國 = 1, 西班牙, 挪威, 烏克蘭, 日本 }
public enum Pet { 狐狸 = 1, 狗, 蝸牛, 馬, 斑馬 }
public enum Drink { 咖啡 = 1, 牛奶, 水, 茶, 橘子汁 }
public enum Smoke { kools = 1, chesterfields, winston, lucky_strike, parlianments }
public class Person
{
public HouseColor housecolor;
public Country country;
public Pet pet;
public Drink drink;
public Smoke smoke;
public bool[] avaliable;
public Person(int h, int c, int p, int d, int s)
{
avaliable = new bool[6] { false, false, false, false, false, false };
switch (h)
{
case 0:
break;
case 1:
housecolor = HouseColor.紅色;
break;
case 2:
housecolor = HouseColor.白色;
break;
case 3:
housecolor = HouseColor.黃色;
break;
case 4:
housecolor = HouseColor.綠色;
break;
case 5:
housecolor = HouseColor.藍色;
break;
}
switch (c)
{
case 0:
break;
case 1:
country = Country.英國;
break;
case 2:
country = Country.西班牙;
break;
case 3:
country = Country.挪威;
break;
case 4:
country = Country.烏克蘭;
break;
case 5:
country = Country.日本;
break;
}
switch (p)
{
case 0:
break;
case 1:
pet = Pet.狐狸;
break;
case 2:
pet = Pet.狗;
break;
case 3:
pet = Pet.蝸牛;
break;
case 4:
pet = Pet.馬;
break;
case 5:
pet = Pet.斑馬;
break;
}
switch (d)
{
case 0:
break;
case 1:
drink = Drink.咖啡;
break;
case 2:
drink = Drink.牛奶;
break;
case 3:
drink = Drink.水;
break;
case 4:
drink = Drink.茶;
break;
case 5:
drink = Drink.橘子汁;
break;
}
switch (s)
{
case 0:
break;
case 1:
smoke = Smoke.kools;
break;
case 2:
smoke = Smoke.chesterfields;
break;
case 3:
smoke = Smoke.winston;
break;
case 4:
smoke = Smoke.lucky_strike;
break;
case 5:
smoke = Smoke.parlianments;
break;
}
}
public Person()
{
avaliable = new bool[6] { false, false, false, false, false, false };
}
public Person(Person p)
{
housecolor = p.housecolor;
country = p.country;
pet = p.pet;
drink = p.drink;
smoke = p.smoke;
avaliable = p.avaliable;
}
public override bool Equals(object obj)//只要有部分元素相等則返回真,非全等
{
if (housecolor == ((Person)obj).housecolor || country == ((Person)obj).country || pet == ((Person)obj).pet || drink == ((Person)obj).drink || smoke == ((Person)obj).smoke)
return true;
return false;
}
public override string ToString()
{
string s = housecolor + " " + country + " " + pet + " " + drink + " " + smoke + " ";
return s;
}
}
}
好吧,我知道你們不喜歡看代碼╮(╯▽╰)╭,下面講一講這個問題的解決思路:
首先,因爲是第一次碰到這樣的問題,你不能假定這個問題的答案只有一個(那樣的話程序半路就可以得出答案了),從百度的結果來看呢,有人說是4個,也有說是7個的,不過這種題目有很多變種的,我們不能確定它是不是隻有唯一的答案(本題的答案是唯一的哦),所有的可能都必須過一遍。
那麼,要想節省時間,就必須把所有不可能的情況提前過濾掉。從問題的規模來看244883200000=(5!)^5,這是考慮所有排列組合的情況下最小的結果,按照先選第一所房子的顏色,再選第二所房子的顏色……再選第五所房子的顏色,然後選第一所房子主人的國籍……………………………………顯然,這是正常的思路╮( ̄▽ ̄")╭
But,正常思路很顯然無法在短時間內解決這個問題,所以,讓我們來點非正常的思路吧:繼續擴大問題的規模!(瘋了!)
沒錯!將問題的規模擴大至5^5^5=3125^5=298023223876953125再來看,剛纔是2400億種情況,現在是光零頭就有8千萬億種情況,也許你不相信,你可以拿計算器按一下。。。(這可是25個5在相乘)
淡定!先不要驚慌,我們來看看這個結果是怎麼出現的。這次,我們先不考慮條件限制,即所有房子的顏色,國籍,寵物,飲料,香菸都可以在五種給定的條件中任選,允許相同(即五所房子可能都是綠色等等),這樣,每生成一所房子就有3125種可能的情況,我們按從左到右的順序依次生成每所房子,這個時候再來考慮條件問題,不滿足則捨棄,滿足則繼續往右生成。
那麼,爲什麼規模擴大到這麼大,反而速度變快了呢?
我們再來分析一下:首先,假設你已經設計了一套方案,方案中第一所房子是給英國人住的,但是根據條件,第一所房子裏住的必須是挪威人,所以緊跟着後面的四所房子的方案都被否決了,也就是說共有4^5^5=1125899906842624種不可能的情況被否決了。1125萬億!
再假設,你的第一所房子的方案滿足了條件,但是第二所房子的顏色不是藍色的,所以,後面的三所房子的方案被否決了,否決量是3^5^5=847288609443,共計8000萬種可能的情況。
依此類推,到第三所房子,否決量是2^5^5=33554432,3300萬!第四所是1^5^5=3125!最後一所房子每次否決量爲1。
所以,每當我們完全確定一個條件的時候,無形之中,就有成千上萬億次的計算量被省略了,例如,當確定第一所房子的主人是挪威人的時候,那麼這所房子的3125種情況中就有5^5-5^4=2500種情況被排除,應用上面的分析,總共即2500*1125萬億=28億億的計算量節省下來了(總數是29億億),再加上往後每所房子都有確定的條件:如第二所房子是藍色的,中間房子的人喝牛奶等等,依次省略計算量爲2500*8000萬=2000億,2500*3000萬=750億……
這樣做下來,剩餘的計算量大概還剩下7000億左右,依舊是比剛開始的2400億的計算量要多,但是別忘記了,我們還只是用了所給的條件的一部分,上面忘了提了,題目中所給條件大致可以分爲三個類型:確定型(根據描述可以完全判定某項屬性的位置)、二維關係型(由二元關係組成)、位置關係型(由位置關係組成)。
對總共給定點的14個條件分類如下:
- 確定型:
- 挪威人住在左邊的第一個房子裏;
- 挪威人住在藍色房子旁邊;
- 中間那個房子裏的人喜歡喝牛奶。
- 二維關係型:
- 英國人住在紅色的房子裏;
- 西班牙人養了一條狗;
- 黃房子裏的人喜歡抽kools牌的香菸;
- 抽winston牌香菸的人養了一個蝸牛;
- 抽lucky strike牌香菸的人喜歡喝橘子汁;
- 烏克蘭人喜歡喝茶;
- 日本人抽parlianments牌的煙;
- 喜歡喝咖啡的人住在綠房子裏;
- 位置關係型:
- 抽chesterfields牌香菸的人與養狐狸的人是鄰居;
- 抽kools牌香菸的人與養馬的人是鄰居;
- 綠房子在象牙白房子的右邊;
剛剛我們只是用到了確定型中的三個條件,而剩餘還有11個條件是我們還沒有用到的。因此,進一步減少計算量就需要對這些條件進行判斷也就是程序中的test_1()、test_2()和test_3()函數。
綜合這些條件,我們就可以將計算量進一步壓縮,從而可以在短時間內解決問題了!