休閑時刻看看神經網絡方面的書,發現了修道士和野人的問題,不禁勾引起我寫算法的欲望,曾經的三只大老虎三只小老虎過河問題、人狼羊白菜過河問題、漢諾塔、哈夫曼等等各種算法瞬間在腦海中約隱約現,修道士和野人問題我以前好像沒有解開,中午吃飯的時候在腦海中重新構造思路,下午耗了點時間把它干掉。(算法不在代碼里,而在思想中;所以盡量不要看我的代碼,而要仔細分析我寫的思路)
題目:
設有3個修道士和3個野人來到河邊, 打算用一條船從河的左岸渡到河的右岸。但該船每次只能裝載兩個人, 在任何岸邊野人的數目都不得超過修道士的人數, 否則修道士就會被野人吃掉。假設野人服從任何一種過河安排, 請問如何規划過河計划才能把所有人都安全地渡過河去。
首先考慮總共有(3+1)*(3+1)= 16 種不同的狀態(因為左岸可以有0,1,2,3個傳教士,左岸可以有0,1,2,3個野人),所以可以考慮使用窮舉法。
使用如下C#程序語言:
int MaxNum = 3; for (int monk = MaxNum; monk >= 0; monk--) { for (int savage = MaxNum; savage >= 0; savage--) { Console.Write("{{" + monk + "," + savage + "},{" + (MaxNum - monk) + "," + (MaxNum - savage) + "}} "); } Console.Write("\n"); }
生成16種狀態圖↓↓↓↓↓↓↓↓↓↓↓
狀態圖含義:
{a,b}:a,左岸修道士數量;b,左岸野人數量。
--------僅考慮左岸傳教士和野蠻人數量(所有狀態圖)------------------------ {3,3} {3,2} {3,1} {3,0} {2,3} {2,2} {2,1} {2,0} {1,3} {1,2} {1,1} {1,0} {0,3} {0,2} {0,1} {0,0}
其中{3,3}是起始狀態圖;{0,0}是終止狀態圖。
去除在任何岸邊野人的人數超過修道士的人數,保留所有可滿足的狀態圖:
--------僅考慮左岸傳教士和野蠻人數量(符合要求狀態圖)---------------------- {3,3} {3,2} {3,1} {3,0} {2,2} {1,1} {0,3} {0,2} {0,1} {0,0}
可知,左岸修道士和野人的總數是先減少、再增加、再減少、再增加...直到左岸的總人數為零(這個由船行動的方向決定的,請仔細思考),所求的最終狀態圖 {0,0},而且每次增減人數最多為2個(條件提示:該船每次只能裝載兩個人),由此可知左岸總人數的增減趨勢是: -1或-2、+1或+2、-1或-2、+1或+2...。
左岸人頭數作為參照:
--------僅考慮左岸傳教士和野蠻人數量(分組)------------------------ heads num:6 {3,3} heads num:5 {3,2} heads num:4 {3,1} {2,2} heads num:3 {3,0} {0,3} heads num:2 {1,1} {0,2} heads num:1 {0,1} heads num:0 {0,0}
將船的停靠岸考慮在內,其中L、R分別表示船在左岸或右岸:
--------僅考慮左岸傳教士和野蠻人數量,以及船的狀態------------------------
heads num:6 {3,3,L}
heads num:5 {3,2,L} {3,2,R}
heads num:4 {3,1,L} {3,1,R} {2,2,L} {2,2,R}
heads num:3 {3,0,L} {3,0,R} {0,3,L} {0,3,R}
heads num:2 {1,1,L} {1,1,R} {0,2,L} {0,2,R}
heads num:1 {0,1,L} {0,1,R}
heads num:0 {0,0,R}
此時,初始狀態圖為 {3,3,L},終止狀態圖為{0,0,R}。
其實講到這里,幾乎答案已經很明顯了。上面已經提到:左岸總人數的增減趨勢是: -1或-2、+1或+2、-1或-2、+1或+2...,假設每種狀態圖只能使用一次,按照-1或-2、+1或+2的循環模式的增長趨勢,可得到如下幾種走法:
--------可能的路徑----------------------------------------
{3,3,L} {3,1,R} {3,2,L} {3,0,R} {3,1,L} {1,1,R} {2,2,L} {0,2,R} {0,3,L} {0,1,R} {0,2,L} {0,0,R}
{3,3,L} {3,1,R} {3,2,L} {3,0,R} {3,1,L} {1,1,R} {2,2,L} {0,2,R} {0,3,L} {0,1,R} {1,1,L} {0,0,R}
{3,3,L} {2,2,R} {3,2,L} {3,0,R} {3,1,L} {1,1,R} {2,2,L} {0,2,R} {0,3,L} {0,1,R} {0,2,L} {0,0,R}
{3,3,L} {2,2,R} {3,2,L} {3,0,R} {3,1,L} {1,1,R} {2,2,L} {0,2,R} {0,3,L} {0,1,R} {1,1,L} {0,0,R}

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApplication1 { public enum State { Left, Right } public class RiverBank { public State state = State.Left; public int monk = 0; public int savage = 0; public bool used = false; } public class Item { public int index = 0; public List<RiverBank> list = new List<RiverBank>(); } class Program { const int MaxNum = 3; static State state = State.Left; public State GetState { get { State r = state; state = (state == State.Left ? State.Right : State.Left); return r; } } static void Add(List<Item> list, int index, params RiverBank[] objs) { foreach (Item i in list) { if (i.index == index) { foreach (RiverBank ii in objs) { bool flag = true; for (int iii = 0; iii < i.list.Count; iii++) { if (i.list[iii].monk == ii.monk && i.list[iii].monk == ii.monk && i.list[iii].state == ii.state) { flag = false; break; } } if (flag) { i.list.Add(ii); } } return; } } Item item = new Item(); item.index = index; item.list.AddRange(objs); list.Add(item); } static void WriteDivInfo(String msg) { Console.WriteLine("\n--------" + msg.PadRight(45, '-')); } static void WriteInfo(List<RiverBank> riverBankList) { foreach (var i in riverBankList) { Console.Write("{" + i.monk + "," + i.savage + "," + (i.state == State.Right ? "R" : "L") + "} "); } Console.WriteLine(); } static void Main(string[] args) { List<Item> list = new List<Item>(); WriteDivInfo("僅考慮左岸傳教士和野蠻人數量(所有狀態圖)"); for (int monk = MaxNum; monk >= 0; monk--) { for (int savage = MaxNum; savage >= 0; savage--) { Console.Write("{" + monk + "," + savage + "}\t"); } Console.Write("\n"); } WriteDivInfo("僅考慮左岸傳教士和野蠻人數量(符合要求狀態圖)"); for (int monk = MaxNum; monk >= 0; monk--) { for (int savage = MaxNum; savage >= 0; savage--) { if (monk == 0 || monk == MaxNum || monk == savage) { int index = monk + savage; //RiverBank p = new RiverBank { monk = monk, savage = savage }; //Add(list, index, p); if (index > 0) { RiverBank p = new RiverBank { monk = monk, savage = savage, state = State.Left }; Add(list, index, p); } if (index < 2 * MaxNum) { RiverBank p = new RiverBank { monk = monk, savage = savage, state = State.Right }; Add(list, index, p); } Console.Write("{" + monk + "," + savage + "}\t"); } else { Console.Write(" \t"); } } Console.Write("\n"); } WriteDivInfo("僅考慮左岸傳教士和野蠻人數量,以及船的狀態"); foreach (Item item in list) { Console.Write("heads num:" + item.index + "\t"); foreach (RiverBank i in item.list) { //Console.Write("{" + i.monk + "," + i.savage + "} "); Console.Write("{" + i.monk + "," + i.savage + "," + (i.state == State.Right ? "R" : "L") + "} "); } Console.Write("\n"); } list.Sort((a, b) => { return a.index < b.index ? 1 : -1; }); WriteDivInfo("可能的路徑"); List<RiverBank> usedList = new List<RiverBank>(); list[0].list[0].used = true; usedList.Add(list[0].list[0]); Cycle(ref list, ref usedList); Console.ReadKey(); } private static void Cycle(ref List<Item> list, ref List<RiverBank> usedList) { RiverBank item = usedList[usedList.Count - 1]; State state = item.state; int monk = item.monk; int savage = item.savage; for (int i = 1; i <= 2; i++) {//±1或±2 for (int j = 0; j <= i; j++) {//每次變化的傳教士數 int k = i - j;//每次變化的野人數 int value = (state == State.Left ? -1 : 1); int m = monk + j * value; int s = savage + k * value; if (m == 0 && s == 0 && state == State.Left) { list[6].list[0].used = true; usedList.Add(list[6].list[0]); WriteInfo(usedList); list[6].list[0].used = false; usedList.Remove(list[6].list[0]); } else if (m >= 0 && s >= 0 && m <= MaxNum && s <= MaxNum) { foreach (var it in list) { foreach (var v in it.list) { if (v.used == false && v.state != state && v.monk == m && v.savage == s) { v.used = true; usedList.Add(v); Cycle(ref list, ref usedList); v.used = false; usedList.Remove(v); } } } } } } } } }