农夫须要把狼、羊、菜和本身运到河对岸去,只有农夫可以划船,并且船比较小,除农夫以外每次只能运一种东西,还有一个棘手问题,就是若是没有农夫看着,羊会偷吃菜,狼会吃羊。请考虑一种方法,让农夫可以安全地安排这些东西和他本身过河。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}"; } } }