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

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

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;


}