深度优先搜索的过河问题应用
问题:
一个警察和一个犯人,一个爸爸一个妈妈,两个儿子两个女儿,他们要过河,有一艘船一次只能坐两个人,爸爸不在妈妈打儿子,妈妈不在爸爸打女儿。警察不在。犯人杀人。怎样才能过河?
解决思路梗概:
1、将八个人转换成八位2进制数,0表示在左岸,1表示在右岸。如警察在右岸就是10000000,所以通过当前位置状态与0x80进行&按位与就能得出警察当前是否已过河。
2、通过给定的条件,如果警察不在犯人会伤人等条件判断是否位置状态安全。只有在安全且之前没有到达过此位置状态的情况下才会到达此位置状态。
3、外层while循环,判断是否已经都过河了,内层for循环每个人和三个能开船的人进行位置状态匹配。两个数组一个栈,一个数组记录已经走过的位置状态,另外一个记录船的位置。栈用来记录符合条件的位置状态,如此条路已经入栈了,但是后面发现走不通,则移除栈顶元素,重新循环。因为数组已经记录了所以走过的情况,故不会走老路。
如果还走不通,继续移除栈顶元素,直到走通。 如下图,A—>D—>A—>C—>A—>B—>F
一句话来说就是,我一条一条的路线去遍历,走着走着走不通了,就退回上一步从新走,因为每走一步我都做了记录(位置和方向),所以不会走到重复的路线,只要有解肯定就能走通。
代码实现
class Program { static void Main(string[] args) { List<int> list = crossRiverProblem(); Console.WriteLine("---------八人从左往右过河的实现------------"); List<string> processList = resultProcess(list); for (int i = 0; i < list.Count; i++) { string resultByte = result(Convert.ToString(list[i], 2)); string resultChi = resultChinese(resultByte); //Console.WriteLine(resultByte + "---" + resultChi + "---->" + processList[i] + "---" + list[i]); Console.WriteLine(resultByte + "---" + resultChi + "---->" + processList[i]); } Console.WriteLine("---------执行结束------------"); Console.ReadKey(); } #region 结果补齐2进制 /// <summary> /// 结果补齐2进制 /// </summary> /// <param name="content"></param> /// <returns></returns> public static string result(string content) { int contentLength = content.Length; switch (contentLength) { case 1: return "0000000" + content; case 2: return "000000" + content; case 3: return "00000" + content; case 4: return "0000" + content; case 5: return "000" + content; case 6: return "00" + content; case 7: return "0" + content; case 8: return content; default: return content; } } #endregion #region 结果转换成汉字 /// <summary> /// 结果转换成汉字 /// </summary> /// <param name="result"></param> /// <returns></returns> public static string resultChinese(string result) { char[] resultChar = result.ToCharArray(); StringBuilder strB = new StringBuilder(); for (int i = 0; i < resultChar.Length; i++) { if (i == 0) { strB.Append(resultChar[i] == '1' ? "警" : "无"); } if (i == 1) { strB.Append(resultChar[i] == '1' ? "犯" : "无"); } if (i == 2) { strB.Append(resultChar[i] == '1' ? "父" : "无"); } if (i == 3) { strB.Append(resultChar[i] == '1' ? "儿" : "无"); } if (i == 4) { strB.Append(resultChar[i] == '1' ? "儿" : "无"); } if (i == 5) { strB.Append(resultChar[i] == '1' ? "母" : "无"); } if (i == 6) { strB.Append(resultChar[i] == '1' ? "女" : "无"); } if (i == 7) { strB.Append(resultChar[i] == '1' ? "女" : "无"); } } return strB.ToString(); } #endregion #region 过河过程描述 /// <summary> /// 过河过程 /// </summary> /// <param name="list"></param> /// <returns></returns> public static List<string> resultProcess(List<int> list) { List<string> processStr = new List<string>(list.Count()); bool chuanLocation = false;//船的初始位置 在左边 就是0 false string firstName = ""; string second = ""; for (int i = 0; i < list.Count(); i++) { firstName = ""; second = ""; if (i == list.Count()-1) { processStr.Add("过河完成"); } else { int newlocation = list[i] ^ list[i + 1]; if (jingLocation(newlocation)) { firstName = "警察"; } if (fanLocation(newlocation)) { second = "犯人"; } if (fuLocation(newlocation)) { if (string.IsNullOrWhiteSpace(firstName)) { firstName = "父亲"; } else { second = "父亲"; } } if (erfLocation(newlocation)) { second = "儿子"; } if (ersLocation(newlocation)) { second = "儿子"; } if (muLocation(newlocation)) { if (string.IsNullOrWhiteSpace(firstName)) { firstName = "母亲"; } else { second = "母亲"; } } if (nvfLocation(newlocation)) { second = "女儿"; } if (nvsLocation(newlocation)) { second = "女儿"; } if (chuanLocation) { if (string.IsNullOrWhiteSpace(second)) { processStr.Add(firstName + "自己从右往左过河"); } else { processStr.Add(firstName + "带着" + second + "从右往左过河"); } } else { if (string.IsNullOrWhiteSpace(second)) { processStr.Add(firstName + "自己从左往右过河"); } else { processStr.Add(firstName + "带着" + second + "从左往右过河"); } } chuanLocation = !chuanLocation; } } return processStr; } #endregion #region 人所在的位置判断 0表示在左岸,1表示在右岸 /// <summary> /// /// </summary> /// <param name="location">当前位置状态</param> /// <returns>false 左岸,true 右岸</returns> // &是按位与,0x80表示十六进制数128(二进制为10000000)---警察 public static bool jingLocation(int location) { return (0 != (location & 0x80)); } // 0x40表示十六进制数64(二进制为01000000)---犯人 public static bool fanLocation(int location) { return (0 != (location & 0x40)); } // 0x20表示十六进制数64(二进制为00100000)---父亲 public static bool fuLocation(int location) { return (0 != (location & 0x20)); } // 0x10表示十六进制数16(二进制为00010000)---第一个儿子 public static bool erfLocation(int location) { return (0 != (location & 0x10)); } // 0x08表示十六进制数8(二进制为00001000)---第二个儿子 public static bool ersLocation(int location) { return (0 != (location & 0x08)); } // 0x04表示十六进制数4(二进制为00000100)---母亲 public static bool muLocation(int location) { return (0 != (location & 0x04)); } // 0x02表示十六进制数2(二进制为00000010)---第一个女儿 public static bool nvfLocation(int location) { return (0 != (location & 0x02)); } // 0x01表示十六进制数1(二进制为00000001)---第二个女儿 public static bool nvsLocation(int location) { return (0 != (location & 0x01)); } #endregion #region 判断是否安全的状态 /// <summary> /// 判断是否安全的状态 /// </summary> /// <param name="location"></param> /// <returns></returns> public static bool isSafe(int location) { //犯人伤人:警察和犯人不在同一侧 犯人一侧有人 if ((fanLocation(location) != jingLocation(location)) && ((fanLocation(location) == fuLocation(location)) || (fanLocation(location) == erfLocation(location)) || (fanLocation(location) == ersLocation(location)) || (fanLocation(location) == muLocation(location)) || (fanLocation(location) == nvfLocation(location)) || (fanLocation(location) == nvsLocation(location)))) { return false; } //父亲伤女儿:父亲和母亲不在一侧 父亲一侧有女儿 if ((fuLocation(location) != muLocation(location)) && ((fuLocation(location) == nvfLocation(location)) || (fuLocation(location) == nvsLocation(location)))) { return false; } //母亲伤儿子:父亲和母亲不在一侧 母亲一侧有儿子 if ((muLocation(location) != fuLocation(location)) && ((muLocation(location) == erfLocation(location)) || (muLocation(location) == ersLocation(location)))) { return false; } // 安全状态 return true; } #endregion #region 过河问题处理 /// <summary> /// 过河问题处理 /// </summary> /// <returns></returns> public static List<int> crossRiverProblem() { Stack<int> stack = new Stack<int>();//创建空栈 符合条件就入栈 走不通了就已经入栈的出栈 stack.Push(0); int[] route = new int[256]; // 用于记录已考虑的状态路径 八个人 每个人两种情况 2的八次方是256 string[] chuan = new string[256]; //用于记录已考虑的路径执行后船的状态(左岸还是右岸)-1是路径未考虑 在左边=false 右边=true // 初始化数组route、chuan for (int i = 0; i < 256; i++) { route[i] = -1; chuan[i] = "-1"; } route[0] = 0; bool chuanLocation = false;//船的初始位置 在左边 就是0 false int n = 0;//用来判断是否走不通 就是循环一圈以后是否毫无进展 int location = stack.Peek();//当前执行的位置状态 while ((route[255] == -1)) { //循环每个人 判断可否载过去 1、2、4、8、16、32、64、128 for (int movers = 1; movers <= 128; movers <<= 1) { n++; #region 如果船的位置 和警察的位置和要过河的人的(movers)位置在一侧 if (((0 != (location & 0x80)) == chuanLocation) && ((0 != (location & 0x80)) == (0 != (location & movers)))) { // 警察载此人过河 计算新状态,^为按位异或,相同为0,不同为1 int newlocation = location ^ (0x80 | movers); //过河后判断状态是否安全 if (isSafe(newlocation)) { //新的状态没有走过 if ((route[newlocation] == -1)) { // 新状态安全且未处理 route[newlocation] = location; // 记录新状态 防止重复走 chuanLocation = !chuanLocation;//改变船的位置 chuan[newlocation] = chuanLocation.ToString(); // 记录新状态下船的位置 n = 0;//能走通 故把n重置为0 stack.Push(newlocation);//新位置状态入栈 location = newlocation;//当前位置状态改为新位置状态 } //新的状态有过 但是船的位置不一样 else if (chuan[newlocation] != "-1" && (chuan[newlocation] != (!chuanLocation).ToString())) { route[newlocation] = location; chuan[newlocation] = (!chuanLocation).ToString(); chuanLocation = !chuanLocation; n = 0; stack.Push(newlocation); location = newlocation; } } } #endregion #region 如果船的位置 和父亲的位置和要过河的人的位置在一侧 if (((0 != (location & 0x20)) == chuanLocation) && ((0 != (location & 0x20)) == (0 != (location & movers))))//如果当前农夫不在左(0) { int newlocation = location ^ (0x20 | movers); if (isSafe(newlocation)) { if ((route[newlocation] == -1)) { route[newlocation] = location; chuan[newlocation] = (!chuanLocation).ToString(); chuanLocation = !chuanLocation; n = 0; stack.Push(newlocation); location = newlocation; } else if (chuan[newlocation] != "-1" && (chuan[newlocation] != (!chuanLocation).ToString())) { route[newlocation] = location; chuan[newlocation] = (!chuanLocation).ToString(); chuanLocation = !chuanLocation; n = 0; stack.Push(newlocation); location = newlocation; } } } #endregion #region 如果船的位置 和母亲的位置和要过河的人的位置在一侧 if (((0 != (location & 0x04)) == chuanLocation) && ((0 != (location & 0x04)) == (0 != (location & movers))))//如果当前农夫不在左(0) { int newlocation = location ^ (0x04 | movers); if (isSafe(newlocation)) { if ((route[newlocation] == -1)) { route[newlocation] = location; chuan[newlocation] = (!chuanLocation).ToString(); chuanLocation = !chuanLocation; n = 0; stack.Push(newlocation); location = newlocation; } else if (chuan[newlocation] != "-1" && (chuan[newlocation] != (!chuanLocation).ToString())) { route[newlocation] = location; chuan[newlocation] = (!chuanLocation).ToString(); chuanLocation = !chuanLocation; n = 0; stack.Push(newlocation); location = newlocation; } } } #endregion #region 判断是否当前状态下走不通 如果走不通就把已存的结果出栈 因为已走的路径已经保存 故重新走不会走老路 if (n > 8) { //已存的位置状态出栈 int loca = stack.Pop(); //Console.WriteLine("移除" + loca); //获取栈顶的位置状态作为最新的位置状态 location = stack.Peek(); n = 0; //改变船的位置 chuanLocation = !chuanLocation; } #endregion } } // 取出来栈里的结果 List<int> resultList = new List<int>(); int stackLength = stack.Count; for (int i = 0; i < stackLength; i++) { resultList.Add(stack.Pop()); } resultList.Reverse(); return resultList; } #endregion }
运行结果
项目下载:http://download.csdn.net/detail/fcydxbd/9884916