花了半个晚上写了一个深度优先求解人狼过河的程序
狼人过河问题:有三个人和三只狼要过河,河里有一只船同时能容纳最多两个生物
假设狼和人都会划船,但如果岸边(不包括船上)狼的数量比人多 ,狼就要吃人
问:怎么把三只狼和三个人安全运输到对岸
1 // mytest.cpp : 定义控制台应用程序的入口点。 2 // 3 4 #include "stdafx.h" 5 #include <windows.h> 6 #include <iostream> 7 #include <string> 8 #include <set> 9 #include <map> 10 #include <vector> 11 using namespace std; 12 13 //打印结果相关函数 14 void printState(char state); 15 void printStates(const vector<char>& vecStates); 16 //根据当前状态,获取下一步可能的所有状态函数 17 void getNextStates(char currState, vector<char>& vecNextStates); 18 19 //检查状态是否符合要求的函数 20 #define RET_IF_NOT_OK(cnt) do { if(cnt < 0 || cnt > 3) return false; } while(0) 21 bool isStateOk(int leftPeopleCnt, int leftWolfCnt, int rightPeopleCnt, int rightWolfCnt) 22 { 23 RET_IF_NOT_OK(leftPeopleCnt); 24 RET_IF_NOT_OK(leftWolfCnt); 25 RET_IF_NOT_OK(rightPeopleCnt); 26 RET_IF_NOT_OK(rightWolfCnt); 27 28 //岸边有人,且人的数量少于狼的数量 29 if (leftPeopleCnt > 0 && leftPeopleCnt < leftWolfCnt) return false; 30 if (rightPeopleCnt > 0 && rightPeopleCnt < rightWolfCnt) return false; 31 32 return true; 33 } 34 35 //根据下一步船所在岸边和岸边狼/人数量生成下一步状态字节 36 //这个函数与getNextStates紧密结合 详细用法请参考该函数调用逻辑 37 char makeState(int leftPeopleCnt, int leftWolfCnt, bool bLeft) 38 { 39 //用一个字节第一位(左数,下同 )表示船在左边(0)还是在右边(1) 40 //用第3、4代表人的数量,第7、8位代表狼的数量(00 ~ 11) 41 //举例:0b00110011 表示左岸有三个人三只狼 船在左边 42 // 0b10100001 表示左岸有三个人一只狼 船在右边 43 char state = 0; 44 45 46 //下一步船在左边 47 if (bLeft) 48 { 49 //传进来的其实是下一步右边岸上人和狼的数量 50 //需要推导出下一步左边岸上的人和儿狼的数量 51 state = state | ((3 - leftPeopleCnt) << 4) | (3 - leftWolfCnt); 52 } 53 else 54 { 55 //下一步船要在右边了 56 state = state | 0x80; 57 state = state | (leftPeopleCnt << 4) | leftWolfCnt; 58 } 59 60 61 return state; 62 } 63 64 //根据当前状态,获取下一步可能的所有状态函数 65 void getNextStates(char state, vector<char>& vecNextStates) 66 { 67 bool bLeft = (state & 0x80) == 0; 68 int leftPepleCnt = (state & 0x30) >> 4, rihgtPeopleCnt = 3 - leftPepleCnt; 69 int leftWolfCnt = (state & 0x03), rightWolfCnt = 3 - leftWolfCnt; 70 if (!isStateOk(leftPepleCnt, leftWolfCnt, rihgtPeopleCnt, rightWolfCnt)) return; 71 72 //定义指针指向当前船所在的岸边和对岸的人和狼数量 73 //这样做的原因是左岸和右岸状态变化逻辑是一样的 可以减少逻辑重复的代码 74 int *pShipPeopleCnt, *pNotShipPeopleCnt, *pShipWolfCnt, *pNotShipWolfCnt; 75 76 if (bLeft) 77 { 78 pShipPeopleCnt = &leftPepleCnt; 79 pShipWolfCnt = &leftWolfCnt; 80 pNotShipPeopleCnt = &rihgtPeopleCnt; 81 pNotShipWolfCnt = &rightWolfCnt; 82 } 83 else 84 { 85 pShipPeopleCnt = &rihgtPeopleCnt; 86 pShipWolfCnt = &rightWolfCnt; 87 pNotShipPeopleCnt = &leftPepleCnt; 88 pNotShipWolfCnt = &leftWolfCnt; 89 } 90 91 //可能变化有五种,一个人/狼单独过河 两个人/狼过河 一只狼和一个人一起过河 92 //根据人的思维,尽量先处理两个生物过河的状态,不行再处理单个生物过河的 93 //两个人/狼过河 94 if ( isStateOk(*pShipPeopleCnt - 2, *pShipWolfCnt, *pNotShipPeopleCnt + 2, *pNotShipWolfCnt) ) 95 { 96 vecNextStates.push_back( makeState(*pShipPeopleCnt - 2, *pShipWolfCnt, !bLeft) ); 97 } 98 99 if ( isStateOk(*pShipPeopleCnt, *pShipWolfCnt - 2, *pNotShipPeopleCnt, *pNotShipWolfCnt + 2) ) 100 { 101 vecNextStates.push_back( makeState(*pShipPeopleCnt, *pShipWolfCnt - 2, !bLeft) ); 102 } 103 104 //一只狼和一个人一起过河 105 if ( isStateOk(*pShipPeopleCnt - 1, *pShipWolfCnt - 1, *pNotShipPeopleCnt + 1, *pNotShipWolfCnt + 1) ) 106 { 107 vecNextStates.push_back( makeState(*pShipPeopleCnt - 1, *pShipWolfCnt - 1, !bLeft) ); 108 } 109 110 //一个人/狼单独过河 111 if ( isStateOk(*pShipPeopleCnt - 1, *pShipWolfCnt, *pNotShipPeopleCnt + 1, *pNotShipWolfCnt) ) 112 { 113 vecNextStates.push_back( makeState(*pShipPeopleCnt - 1, *pShipWolfCnt, !bLeft) ); 114 } 115 116 if ( isStateOk(*pShipPeopleCnt, *pShipWolfCnt - 1, *pNotShipPeopleCnt, *pNotShipWolfCnt + 1) ) 117 { 118 vecNextStates.push_back( makeState(*pShipPeopleCnt, *pShipWolfCnt - 1, !bLeft) ); 119 } 120 121 //如果要避免一些看起来比较愚蠢的走法,可以在这里加上对状态优先级排序的逻辑 122 } 123 124 //狼人过河问题:有三个人和三只狼要过河,河里有一只船同时能容纳最多两个生物 125 //假设狼和人都会划船,但如果岸边(不包括船上)狼的数量比人多 ,狼就要吃人 126 //问:怎么把三只狼和三个人安全运输到对岸 127 int dualLangRenOnce(char currState, vector<char>& vecStates, set<char> closeTab) 128 { 129 //如果当前状态是目标状态,则退出递归 130 if ((currState & 0x33) == 0) return 0; 131 132 vector<char> vecNextState; 133 //获取下个状态,即考虑怎么安排狼和人过河 134 getNextStates(currState, vecNextState); 135 for (size_t i = 0; i < vecNextState.size(); i++) 136 { 137 if ( closeTab.find(vecNextState[i]) == closeTab.end() ) 138 { 139 closeTab.insert(vecNextState[i]); 140 vecStates.push_back(vecNextState[i]); 141 //如果打算找全部可能解 这里就不需要return了 142 if (dualLangRenOnce(vecNextState[i], vecStates, closeTab) == 0) return 0; 143 vecStates.pop_back(); 144 } 145 } 146 147 return 1; 148 } 149 150 void dualLangRen() 151 { 152 //用一个字节第一位(左数,下同 )表示船在左边还是在右边 153 //用第3、4代表人的数量,第7、8位代表狼的数量(00 ~ 11) 154 //举例:0b00110011 表示左岸有三个人三只狼 船在左边 155 // 0b10100001 表示左岸有三个人一只狼 船在右边 156 char state = 0x33; 157 //vector记录状态变化过程 158 vector<char> vecStates; 159 //set记录已经历过的状态,以免重复尝试造成无尽递归 160 set<char> closeTab; 161 162 vecStates.push_back(state); 163 closeTab.insert(state); 164 if (dualLangRenOnce(state, vecStates, closeTab) == 0) 165 { 166 printStates(vecStates); 167 } 168 else 169 { 170 cout << "问题无解!" << endl; 171 } 172 173 return; 174 } 175 176 void printState(char state) 177 { 178 bool bLeft = (state & 0x80) == 0; 179 int leftPepleCnt = (state & 0x30) >> 4; 180 int leftWolfCnt = (state & 0x03); 181 182 printf("人%d 狼%d %s 人%d 狼%d", leftPepleCnt, leftWolfCnt, (bLeft ? "左": "右"), 3 -leftPepleCnt, 3 - leftWolfCnt); 183 } 184 185 void printStates(const vector<char>& vecStates) 186 { 187 for(vector<char>::const_iterator it = vecStates.begin(); it != vecStates.end(); it++) 188 { 189 printState(*it); 190 printf("\n"); 191 } 192 } 193 194 int _tmain(int argc, _TCHAR* argv[]) 195 { 196 double start = GetTickCount(); 197 dualLangRen(); 198 199 //我自己机在win7 vs2008环境 debug模式是16毫秒 release不到1毫秒 200 printf("总共耗时 %f 毫秒\n", GetTickCount() - start); 201 202 //挂屏 203 system("pause"); 204 return 0; 205 }
运行结果:
这是最近第二次写了,复习一遍数据结构之后的作品
之前没有复习前写过另一版,逻辑比较混乱,有空再贴上来对比
~~积土成山,风雨兴焉;积水成渊,蛟龙生焉;~~~