农夫过河问题(栈实现、深度优先)

农夫过河问题(栈实现、深度优先)

1. 问题描述
2. 问题分析
3. 代码分析

参考
农夫过河 (BFS)(队列)
C++ 用回溯法求解迷宫问题(栈)

1.问题描述

农夫带着白菜,羊,狼希望从河的南岸到达河的北岸,唯一的工具是一条小船,并且只有农夫会划船,一次最多只能带一样东西到对岸,请问,农夫要怎么样才能带他的东西过河呢?其中,所携带的东西中如果羊和白菜单独在一起,羊将吃掉白菜;羊和狼单独在一起,狼将吃掉羊。

2.问题分析

1.准备:将每一刻的状态用0,1表示,比如农夫在南岸为0,在北岸为1。
创建一个state结构体表示当前农夫、菜、羊、狼分布情况

struct state {
    int loc[4];//loc[0]为农夫,以此为菜、羊、狼
};

将这四位状态在一起变成二进制,就可以用一个整数表示
如:

 int test1 = 8 * (now.loc[0]) + 4 * (now.loc[1]) + 2 * (now.loc[2]) + 1 * (now.loc[3]);//now表示当前状态,将状态转化为十进制

好了,现在我们可以表示每次移动后的状态了,创建一个route数组用来记录移动路径,因为每一种状态可以用一个小于16的数表示

int route[16]; //存储路径,每一个存从哪过来的

打个比方:当前在起点为 0000,我们知道农夫接下来有四种选择,写个函数通过每个选择是否可行。
比如带菜过去,接下来的状态就为1100(12),通过函数safe()判断,不行进行下一个

int FarmerProblem::farmer(state st) const
{
    return  !(st.loc[0] == 0);
}

int FarmerProblem::cabbage(state st) const
{
    return  !(st.loc[1] == 0);
}

int FarmerProblem::sheep(state st) const
{
    return  !(st.loc[2] == 0);
}

int FarmerProblem::wolf(state st) const
{
    return  !(st.loc[3] == 0);
}

bool FarmerProblem::safe(state st)
{   //农夫没有看牢白菜,并且让羊和白菜在一起,危险!
    if (farmer(st) != cabbage(st) && cabbage(st) == sheep(st)) return false;
    //农夫没有看牢羊,并且让羊和狼在一起,危险!
    if (farmer(st) != sheep(st) && sheep(st) == wolf(st)) return false;
    //其他情况,安全!
    return true;
}

判断可以带羊过去,羊过去之后状态为1010(10)
那么则有

route[10]=0;//表示10这个状态是从0来的

2.结构
利用stack存储state状态,若接下来的状态为真就压栈。
如果一个状态的四种选择都不行,就把当前状态出栈,退回到上一个状态再讨论,这里我们因为用route记录是否走过。所以退到上一个状态后不用担心再走一样的路。
如果可以走通就会沿着一个方向深入直到没路退回或者到1111这个状态(深度优先)。

3. 代码分析*(全部代码)

#include<iostream>
#include<stack>


using namespace std;

using namespace std;
//vector<int> loc(4,0);//int loc[4]; //这里使用4个整型值标记
struct state {
    int loc[4];
};
class FarmerProblem {
public:
    bool safe(state st);//检查当前状态是否安全
    int  farmer(state st) const; //农夫所在位置
    int cabbage(state st)const;//菜所在位置
    int sheep(state st)const;//羊所在位置
    int wolf(state st)const;//狼所在位置
    void doit();//寻找移动序列
    void display() const;//输出可能的移动序列


private:
    int route[16]; //存储路径或者

};

//loc中位依次为农夫,白菜,羊,狼。


int FarmerProblem::farmer(state st) const
{
    return  !(st.loc[0] == 0);
}

int FarmerProblem::cabbage(state st) const
{
    return  !(st.loc[1] == 0);
}

int FarmerProblem::sheep(state st) const
{
    return  !(st.loc[2] == 0);
}

int FarmerProblem::wolf(state st) const
{
    return  !(st.loc[3] == 0);
}

bool FarmerProblem::safe(state st)
{   //农夫没有看牢白菜,并且让羊和白菜在一起,危险!
    if (farmer(st) != cabbage(st) && cabbage(st) == sheep(st)) return false;
    //农夫没有看牢羊,并且让羊和狼在一起,危险!
    if (farmer(st) != sheep(st) && sheep(st) == wolf(st)) return false;
    //其他情况,安全!
    return true;
}

void FarmerProblem::doit()
{//请编写找一个可实现序列的算法

    stack<state> st;
    state now;//now表示现在
    int d = 0;
    for (int i = 1; i <= 15; i++)//初始化route数组
        route[i] = -1;
    
    for (int i = 0; i < 4; i++) {//设置初值
        now.loc[i] = 0;
    }
    st.push(now);//将初始状态放入栈
    route[0] = 0;//标记初始状态以经过
    //int test1 = 0, test2 = 0;//test1表示当前状态(十进制),test2表示假设状态(十进制)
    while (!st.empty() && route[15] == -1)//如果栈不空或者都没有到达对岸就继续搜索
    {
        now = st.top();//获取栈顶当前状态
        int test1 = 8 * (now.loc[0]) + 4 * (now.loc[1]) + 2 * (now.loc[2]) + 1 * (now.loc[3]);//将状态转化为十进制

        while (d < 4) {//农夫有四种选择,带菜、羊、狼或者不带
            
            if (d >= 1) {//d>1表示带菜、羊、狼中的一个
                state c = now;//为了不破坏状态用c来代替
                if (now.loc[0] == now.loc[d])//如果农夫和其中一个对象在同一个地方
                {
                    if (c.loc[0] == 0)//如果是0就过河换为1
                    {
                        c.loc[0] = 1;
                        c.loc[d] = 1;
                    }
                    else//如果是1就过河换为0
                    {
                        c.loc[0] = 0;
                        c.loc[d] = 0;
                    }
                    int test2 = 8 * (c.loc[0]) + 4 * (c.loc[1]) + 2 * (c.loc[2]) + 1 * (c.loc[3]);//将假设状态转化为十进制

                    if (safe(c) && route[test2] == -1)//如果假设情况过河后状态是合法的同时这种状态还没有经过(判断没有经过可以保证一种状态只经过一次)
                    {
                        st.push(c);//将这种状态放入栈中
                        route[test2] = test1;//标记这种状态已经经过
                        d = 0;//假设状态可行,可以进行下一个状态的判断了
                        break;
                    }
                }
       
            }

            if (d == 0) {
                state c = now;//这种情况是考虑农夫不带任何东西过河,与上面判断情况相同
                if (c.loc[0] == 0)
                {
                    c.loc[0] = 1;
                }
                else
                {
                    c.loc[0] = 0;
                }
                int test2 = 8 * (c.loc[0]) + 4 * (c.loc[1]) + 2 * (c.loc[2]) + 1 * (c.loc[3]);//将假设状态转化为十进制
                if (safe(c) && route[test2] == -1)
                {
                    st.push(c);
                    route[test2] = test1;
                    d = 0;
                    break;
                }
            }
            d++;
        }

        if (d == 4) {//四种选择都不行,出栈回到上一个状态
            st.pop();
        }

    }
    if (route[15] != -1)//如果最终全部都过了河,倒序输出过河步骤
    {
        display();
    }



}


void FarmerProblem::display() const
{  //显示移动路径
    cout << "15   1111" << endl;//15情况没有后继直接输出
    for (int i = 15; i > 0; i = route[i])
    {
        cout << route[i] << "   ";//输出该状态对应前驱
        if (route[i] < 10)
            cout << ' ';
        switch (route[i])//输出该状态十进制数对应的二进制数
        {
        case 0:cout << "0000" << endl; break;
        case 1:cout << "0001" << endl; break;
        case 2:cout << "0010" << endl; break;
        case 3:cout << "0011" << endl; break;
        case 4:cout << "0100" << endl; break;
        case 5:cout << "0101" << endl; break;
        case 6:cout << "0110" << endl; break;
        case 7:cout << "0111" << endl; break;
        case 8:cout << "1000" << endl; break;
        case 9:cout << "1001" << endl; break;
        case 10:cout << "1010" << endl; break;
        case 11:cout << "1011" << endl; break;
        case 12:cout << "1100" << endl; break;
        case 13:cout << "1101" << endl; break;
        case 14:cout << "1110" << endl; break;
        }
        if (i == 0)
            break;
    }
}

int main() {

    FarmerProblem a;
    a.doit();

    return 0;


}
  • 12
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
农夫过河问题是一个经典的搜索问题,可以使用深度优先遍历算法进行求解。下面是一种可能的实现方法: 首先,我们定义节点的状态表示为一个四元组 (farmer, wolf, goat, cabbage),其中 farmer 表示农夫的位置,0 表示在起点岸边,1 表示在终点岸边;wolf, goat, cabbage 分别表示狼、羊、白菜的位置,取值同 farmer。例如,(0, 0, 0, 0) 表示初始状态,农夫、狼、羊、白菜都在起点岸边。 接下来,我们可以使用递归函数 dfs(state, path) 进行深度优先遍历。其中,state 表示当前节点的状态,path 表示从初始状态到当前状态经过的路径。在每一次递归时,我们需要判断当前状态是否是目标状态 (1, 1, 1, 1),如果是,则输出路径并返回;否则,我们需要枚举所有可能的下一步状态,并继续递归搜索,直到找到目标状态或者搜索完所有可能的状态。 具体实现如下: ```python def dfs(state, path): if state == (1, 1, 1, 1): print(path) return farmer, wolf, goat, cabbage = state for new_state in get_next_states(state): new_farmer, new_wolf, new_goat, new_cabbage = new_state if (new_wolf == new_goat and new_farmer != new_wolf) or (new_goat == new_cabbage and new_farmer != new_goat): continue # 不合法状态,跳过 dfs(new_state, path + [(new_state, farmer)]) def get_next_states(state): farmer, wolf, goat, cabbage = state next_states = [] if farmer == 0: if wolf == 0: next_states.append((1, 1, goat, cabbage)) if goat == 0: next_states.append((1, wolf, 1, cabbage)) if cabbage == 0: next_states.append((1, wolf, goat, 1)) next_states.append((1, wolf, goat, cabbage)) else: if wolf == 1: next_states.append((0, 0, goat, cabbage)) if goat == 1: next_states.append((0, wolf, 0, cabbage)) if cabbage == 1: next_states.append((0, wolf, goat, 0)) next_states.append((0, wolf, goat, cabbage)) return next_states # 测试 dfs((0, 0, 0, 0), []) ``` 该算法的时间复杂度为 O(4^d),其中 d 表示最短路径的长度(即答案),因为在最坏情况下,每个状态都可以扩展出 4 个状态。实际运行时间取决于搜索树的形状和目标状态的位置,可能会比较慢。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值