zoukankan      html  css  js  c++  java
  • 花了半个晚上写了一个深度优先求解人狼过河的程序

     

    狼人过河问题:有三个人和三只狼要过河,河里有一只船同时能容纳最多两个生物
    假设狼和人都会划船,但如果岸边(不包括船上)狼的数量比人多 ,狼就要吃人
    问:怎么把三只狼和三个人安全运输到对岸

      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("
    ");
    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 毫秒
    ", GetTickCount() - start);
    201 
    202     //挂屏
    203     system("pause");
    204     return 0;
    205 }

    运行结果:

    这是最近第二次写了,复习一遍数据结构之后的作品

    之前没有复习前写过另一版,逻辑比较混乱,有空再贴上来对比

  • 相关阅读:
    SQL 游标使用实例 no
    C# DataTable 转换成JSON数据 no
    css设置滚动条的样式 no
    C# DataTable 转换成JSON数据 no
    springboot项目打包jar 并打包为exe启动
    springboot 项目启动自动打开浏览器访问网站设置
    springboot启动创建系统托盘及功能
    关于Web Service
    最近的我
    C++ wstring和string相互转换
  • 原文地址:https://www.cnblogs.com/kingstarer/p/5931569.html
Copyright © 2011-2022 走看看