养鱼问题

今天看到这里讨论养鱼问题,也试试作为娱乐。据说是爱因斯坦谜题。

 

题目:

1. 有5栋5种颜色的房子
2. 每一位房子的主人国籍都不同
3. 这五个人每人只喝一个牌子的饮料,只抽一个牌子的香烟,只养一种宠物
4. 没有人有相同的宠物,抽相同牌子的烟,喝相同牌子的饮料
已知条件:
1. 英国人住在红房子里
2. 瑞典人养了一条狗
3. 丹麦人喝茶
4. 绿房子在白房子的左边
5. 绿房子主人喝咖啡
6. 抽pallmall烟的人养了一只鸟
7. 黄房子主人抽dunhill烟
8. 住在中间房子的人喝牛奶
9. 挪威人住在第一间房子
10.抽混合烟的人住在养猫人的旁边
11.养马人住在抽dunhill烟人的旁边
12.抽bluemaster烟的人喝啤酒
13.德国人抽prince烟
14.挪威人住在蓝房子旁边
15.抽混合烟的人的邻居喝矿泉水
问题:谁养鱼?

英文原题:
The Einstein Puzzle
 
There are 5 houses in five different colors. They are lined up in a row side by side.
In each house lives a person with a different nationality.
These 5 owners drink a certain drink, smoke a certain brand of tobacco and keep a certain pet.
No owners have the same pet, smoke the same tobacco, or drink the same drink.
As you look at the 5 houses from across the street, the green house is adjacent to the left of the white house
The Big Question is:
Who owns the Fish?
 
CLUES:
1、The Brit lives in the red house
2、The Swede keeps dogs as pets
3、The Dane drinks tea
4、The green house is on the immediate left of the white house as you stare at the front of the 5 houses
5、The green house owner drinks coffee
6、The person who smokes Pall Mall raises birds
7、The owner of the yellow house smokes Dunhill
8、The man living in the house right in the center drinks milk
9、The Norwegian lives in the first house
10、The man who smokes Blends lives next to the one who keeps cats
11、The man who keeps horses lives next to the one who smokes Dunhill
12、The owner who smokes Bluemaster drinks juice
13、The German smokes Prince
14、The Norwegian lives next to the blue house
15、The man who smokes Blend has a neighbor who drinks water.

 

当然不是采用穷举的办法,而是按照人的思路。

关键是忽略人的概念,把人和物放在一个层次看。很多条件都可以忽略其中提到的人,直接建立两件事物的联系。

执行结果比100多秒那么离谱要好很多:

 

德国人养鱼
Time used: 00:00:00.0468750

 

 namespace WhoFeedFish { class Program { public static void WriteLine(string message) { #if DEBUG Console.WriteLine(message); #endif } public static void Write(string message) { #if DEBUG Console.Write(message); #endif } static void Main(string[] args) { var begin = DateTime.Now; new Program().Solve(); Console.WriteLine(string.Format("Time used: {0}", DateTime.Now - begin)); Console.ReadLine(); } List m_Items = new List(); List m_Conditions = new List(); Condition MakeCondition(ItemId a, ItemId b, Relation relation) { Condition result = new Condition(); result.Relation = relation; result.First = m_Items[(int)a]; result.Second = m_Items[(int)b]; return result; } public const int MaxItem = 5; void Solve() { int i = 0; foreach (ItemType type in Enum.GetValues(typeof(ItemType))) { for (int j = 0; j < MaxItem; j++) { var id = (ItemId)i; m_Items.Add(new Item() { Type = type, Id = id }); ++i; } } m_Conditions.Add(MakeCondition(ItemId.英国, ItemId.红, Relation.Same)); m_Conditions.Add(MakeCondition(ItemId.瑞典, ItemId.狗, Relation.Same)); m_Conditions.Add(MakeCondition(ItemId.丹麦, ItemId.茶, Relation.Same)); m_Conditions.Add(MakeCondition(ItemId.绿, ItemId.白, Relation.Prior)); m_Conditions.Add(MakeCondition(ItemId.绿, ItemId.咖啡, Relation.Same)); m_Conditions.Add(MakeCondition(ItemId.PallMall, ItemId.鸟, Relation.Same)); m_Conditions.Add(MakeCondition(ItemId.黄, ItemId.Dunhill, Relation.Same)); m_Items[(int)ItemId.牛奶].Location = 2; //中间房子喝牛奶 m_Items[(int)ItemId.挪威].Location = 0; //第一是0 m_Conditions.Add(MakeCondition(ItemId.Blends, ItemId.猫, Relation.Neighbour)); m_Conditions.Add(MakeCondition(ItemId.马, ItemId.Dunhill, Relation.Neighbour)); m_Conditions.Add(MakeCondition(ItemId.BlueMaster, ItemId.啤酒, Relation.Same)); m_Conditions.Add(MakeCondition(ItemId.德国, ItemId.Prince, Relation.Same)); m_Conditions.Add(MakeCondition(ItemId.挪威, ItemId.蓝, Relation.Neighbour)); m_Conditions.Add(MakeCondition(ItemId.Blends, ItemId.水, Relation.Neighbour)); int allPossible = 125; //最大可能 //谁养鱼,就是问,鱼和Type是Person的Item Location相同 //如果能够继续解决,就一直循环 while (allPossible > 25) //25就是解决了 { int newPossible = OnePass(); if (newPossible < allPossible) { allPossible = newPossible; } else { break; } } int fishLocation = m_Items[(int)ItemId.鱼].Location; if (fishLocation == -1) { Console.WriteLine("can't solve"); return; } foreach (var item in m_Items) { if (item.Type == ItemType.Person && item.Location == fishLocation) { //解决 Console.WriteLine(string.Format("{0}人养鱼", item.Id)); return; } } Console.WriteLine("can't solve"); } //计算一次 int OnePass() { int result = Debug(); foreach (var c in m_Conditions) { //计算Neighbour if (c.Relation == Relation.Neighbour) { CalcNeibour(c.First, c.Second); } //计算Prior else if (c.Relation == Relation.Prior) { CalcPrior(c.First, c.Second); } //计算Same //计算Prior else if (c.Relation == Relation.Same) { CalcSame(c.First, c.Second); } } ReduceByKnown(); return result; } int m_Passcount; private int Debug() { int allPossible = 0; foreach (var item in m_Items) { allPossible += item.PossibleLocation.Count; DebugItem(item); } WriteLine(string.Format("pass {0}: all possible is {1}", m_Passcount, allPossible)); WriteLine("======================================="); ++m_Passcount; return allPossible; } public static void DebugItem(Item item) { Write(string.Format("{0} locations: ", item.Id)); foreach (var i in item.PossibleLocation) { Write(i.ToString()); Write(" "); } WriteLine(""); } List GetOtherSameTypeItem(Item item) { List result = new List(); int start = ((int)item.Id) / MaxItem; for (int i = start * MaxItem; i < start * MaxItem + MaxItem; i++) { if (m_Items[i] != item) result.Add(m_Items[i]); } return result; } private void ReduceByKnown() { foreach (var item in m_Items) { //同类型元素排除位置已经确定的 if (item.PossibleLocation.Count == 1) { foreach (var other in GetOtherSameTypeItem(item)) { other.SetPossibleLocation(Exclude(other.PossibleLocation, item.Location), string.Format("exclude {0}", item.Id)); } } } //如果一种类型的某一个位置只出现在一个元素的可能性中,则此元素的可能位置就只能是这个位置 foreach (ItemType type in Enum.GetValues(typeof(ItemType))) { for (int i = 0; i < MaxItem; i++) { var query = from c in m_Items where c.Type == type && c.PossibleLocation.Contains(i) select c; if (query.Count() == 1) { if (query.First().Location == -1) query.First().SetPossibleLocation(new List() { i }, string.Format("{0} set only location {1}", type, i)); } } } } private void CalcSame(Item a, Item b) { //前一个元素位置必须是后一个-1 //前一个的可能位置是后一个可能位置全部-1 //与原先计算的可能位置相交,减少可能位置 a.SetPossibleLocation(Intersect(b.PossibleLocation, a.PossibleLocation), string.Format("Calc Same {0}", b.Id)); b.SetPossibleLocation(Intersect(a.PossibleLocation, b.PossibleLocation), string.Format("Calc Same {0}", a.Id)); } public List GetPrior(List possible, int relative) { List result = new List(); foreach (var p in possible) { if (p + relative < MaxItem && p + relative >= 0) result.Add(p + relative); } return result; } //a < b private void CalcPrior(Item a, Item b) { //前一个元素位置必须是后一个-1 //前一个的可能位置是后一个可能位置全部-1 //与原先计算的可能位置相交,减少可能位置 a.SetPossibleLocation(Intersect(GetPrior(b.PossibleLocation, -1), a.PossibleLocation), string.Format("Calc prior {0}", b.Id)); b.SetPossibleLocation(Intersect(GetPrior(a.PossibleLocation, 1), b.PossibleLocation), string.Format("Calc next {0}", a.Id)); } public List GetNeibour(List possible) { List result = new List(); foreach (var p in possible) { if (p + 1 < MaxItem) { if (!result.Contains(p + 1)) result.Add(p + 1); } if (p - 1 >= 0) { if (!result.Contains(p - 1)) result.Add(p - 1); } } return result; } private void CalcNeibour(Item a, Item b) { //相邻元素位置必须是+1或者-1 //即后一个元素的可能位置派生出2倍的可能位置 //与原先计算的可能位置相交,减少可能位置 a.SetPossibleLocation(Intersect(GetNeibour(b.PossibleLocation), a.PossibleLocation), string.Format("Calc Neibour {0}", b.Id)); b.SetPossibleLocation(Intersect(GetNeibour(a.PossibleLocation), b.PossibleLocation), string.Format("Calc Neibour {0}", a.Id)); } private List Intersect(List a, List b) { List result = new List(); foreach (var i in a) { if (b.Contains(i)) result.Add(i); } return result; } private List Exclude(List a, int b) { List result = new List(a); result.Remove(b); if (result.Count == 0) WriteLine("wrong"); return result; } bool HasSameItem(List first, List second) { foreach (var item in first) { if (second.Contains(item)) return true; } return false; } } public enum ItemType { Person, Color, Drink, Smoke, Pet } public enum ItemId { 英国, 瑞典, 丹麦, 挪威, 德国, 红, 绿, 白, 黄, 蓝, 茶, 咖啡, 牛奶, 啤酒, 水, PallMall, Dunhill, Blends, BlueMaster, Prince, 狗, 鸟, 猫, 马, 鱼 } public class Item { public ItemType Type { get; set; } public ItemId Id { get; set; } int m_Location = -1; public int Location { get { return m_Location; } set { m_Location = value; if (m_Location != -1) { if (m_PossibleLocation.Count != 1) { m_PossibleLocation.Clear(); m_PossibleLocation.Add(m_Location); } } } } List m_PossibleLocation = new List(); public List PossibleLocation { get { return m_PossibleLocation; } } public void SetPossibleLocation(List value, string message) { if (value.Count < m_PossibleLocation.Count) { Program.WriteLine("reduced by :" + message); Program.Write("old value "); Program.DebugItem(this); m_PossibleLocation = value; m_PossibleLocation.Sort((x, y) => { return x - y; }); if (m_PossibleLocation.Count == 1) m_Location = m_PossibleLocation[0]; Program.Write("new value "); Program.DebugItem(this); } } public Item() { for (int i = 0; i < Program.MaxItem; i++) { m_PossibleLocation.Add(i); } } } public class Pair { public T First { get; set; } public T Second { get; set; } } public enum Relation { Same, Prior, Neighbour } public class Condition { public Relation Relation { get; set; } public Item First { get; set; } public Item Second { get; set; } } }

你可能感兴趣的:(养鱼问题)