農夫須要把狼、羊、菜和本身運到河對岸去,只有農夫可以划船,並且船比較小,除農夫以外每次只能運一種東西,還有一個棘手問題,就是若是沒有農夫看着,羊會偷吃菜,狼會吃羊。請考慮一種方法,讓農夫可以安全地安排這些東西和他本身過河。c#
問題很簡單,但如何用計算機求解呢。安全
農夫渡河從本質上是一種狀態的改變。this
有農夫、狼、羊、菜四個個體,任什麼時候刻每一個個體的狀態只有一種,每一個個體有兩種狀態(沒有過河、已通過河)。3d
依次用4位分別表明農夫、狼、羊、菜,0表示未過河,1表示已過河。則起始狀態爲0000,目標狀態爲1111。code
優先級:
農夫過河時,優先帶貨物;回返時優先不帶貨物。blog
可能有16(2^4)種狀態,但由於狼吃羊,羊吃菜的限制,部分狀態是沒法成立的。ip
是以0000爲根的一顆狀態樹,當某個葉子節點是狀態1111,則表示從根到這個葉子節點之間的狀態序列是本問題的一個解,須要避免出現重複狀態致使死循環。
方法1: 每一個狀態有8種可選動做,轉換爲8個新狀態,但在特定狀態下某些動做是無效的。get
定義8種狀態轉換運算,對當前節點遍歷執行這8種運算,找到全部子節點string
方法2: 依據當前狀態,判別它全部可選的動做(最多4種)。it
class Program { static void Main(string[] args) { var original = new State(); var path = new List<State>(); path.Add(original); int count = 0; Search(path, ref count); Console.ReadKey(); } private static void Search(List<State> path, ref int count) { var cur = path[path.Count - 1]; if (cur.Man && cur.Wolf && cur.Vegetable && cur.Sheep) { count++; Console.WriteLine($"解{count}:"); path.ForEach((a) => { Console.WriteLine(a.Action); }); return; } if (cur.Man) { Switch(path, ref count, cur, "返回"); } else { Switch(path, ref count, cur, "過河"); } } private static void Switch(List<State> path, ref int count, State cur, string action) { var newState = cur.Copy(); newState.Man = !newState.Man; newState.Action = "獨自" + action; Action(path, ref count, newState); if (cur.Sheep == cur.Man) { newState.Sheep = !newState.Sheep; newState.Action = "帶着羊" + action; Action(path, ref count, newState); newState.Sheep = !newState.Sheep; } if (cur.Wolf == cur.Man) { newState.Wolf = !newState.Wolf; newState.Action = "帶着狼" + action; Action(path, ref count, newState); newState.Wolf = !newState.Wolf; } if (cur.Vegetable == cur.Man) { newState.Vegetable = !newState.Vegetable; newState.Action = "帶着菜" + action; Action(path, ref count, newState); newState.Vegetable = !newState.Vegetable; } } private static void Action(List<State> path, ref int count, State newState) { if (newState.IsOk) { foreach (var item in path) { if (item.Equals(newState)) { return; } } path.Add(newState); Search(path, ref count); path.RemoveAt(path.Count - 1); } } //false 表示未過河, true表示已過河 private class State { public bool Man { get; set; } public bool Wolf { get; set; } public bool Sheep { get; set; } public bool Vegetable { get; set; } public string Action { get; set; } public bool IsOk { get { if (Wolf == Sheep && Wolf != Man) { return false; } if (Sheep == Vegetable && Sheep != Man) { return false; } return true; } } public State Copy() { return new State { Man = this.Man, Wolf = this.Wolf, Sheep = this.Sheep, Vegetable = this.Vegetable }; } public bool Equals(State newState) { return (this.Man == newState.Man && this.Wolf == newState.Wolf && this.Sheep == newState.Sheep && this.Vegetable == newState.Vegetable); } } }
遍歷圖,找出全部從0000到1111的路徑
一個狀態只能通過一次。
class Program { static void Main(string[] args) { //找到全部的狀態 var states = new List<Vertex>(); for (int i = 0; i < 16; i++) { var temp = i >> 1; if (temp == 0b011 || temp == 0b100) { continue; } var temp2 = i & 0b1011; if (temp2 == 0b1000 || temp2 == 0b0011) { continue; } states.Add(new Vertex { State = i }); } var steps = new List<Step>(); Search(states[0], states, steps); Console.ReadKey(); } private static void Search(Vertex cur, List<Vertex> states, List<Step> steps) { if(cur.State == 0b1111) { Console.WriteLine(); steps.ForEach((a)=> { Console.WriteLine(a.Description); }); return; } cur.HasVisited = true; foreach (var item in states) { if (!item.HasVisited && CanBeNext(cur.State,item.State)) { steps.Add(new Step { From = cur.State,To = item.State }); Search(item, states, steps); steps.RemoveAt(steps.Count - 1); } } cur.HasVisited = false; } private static bool CanBeNext(int a,int b) { if (b == 0) { return false; } if((a^b)>>3 == 0) { return false; } var man = a >> 3; var temp = (a & 0b0111)^(b & 0b0111); if(temp == 0 || temp == 0b100 && (a & 0b0100)>>2 == man || temp == 0b010 && (a & 0b0010)>>1 == man || temp == 0b001 && (a & 0b0001) == man) { return true; } return false; } private class Vertex { public int State { get; set; } public bool HasVisited { get; set; } } private class Step { public int From { get; set; } public int To { get; set; } public string Description { get { var action = From > 7 ? "返回" : "過河"; var temp = (From & 0b0111) ^ (To & 0b0111); if(temp == 0) { action = "獨自" + action; } if (temp == 0b100) { action = "帶着狼" + action; } if (temp == 0b010) { action = "帶着羊" + action; } if (temp == 0b001) { action = "帶着菜" + action; } return $"{PrintState(From)}--{action} --> {PrintState(To)}"; } } private string PrintState(int a) { return $"{a / 8}{a % 8 / 4}{a % 4 / 2}{ a % 2}"; } } }