過河問題-狼羊人菜

/*
*功能:解決狼羊人過河問題
*作者:王文堃
*作者郵箱:[email protected]
*創建時間:2016/4/5
*/

/*
問題描述:有一個人帶着一匹狼、一頭羊和白菜要過河
已知人每次過河只能帶一樣東西,狼和羊不能單獨在一起
羊和菜不能單獨在一起,求人過河的方案有幾種?

問題抽象:分別用m、w、g、c來表示人(men)、狼(wolf)、羊(goat)、菜(cabbage)

問題的解決步驟:
1.求出人狼羊菜在河兩岸的所有可能性,排列組合共16種情況,分別是
c40:(mwgc|)
c41:(wgc|m)、(mgc|w)、(mwc|g)、(mwg|c)
c42:(gc|mw)(wc|mg)(wg|mc)(mc|wg)(mg|wc)(mw|gc)
c43:(m|wgc)(w|mgc)(g|mwc)(c|mwg)
c44:(|mwgc)
----------------------------------------------------------
2.根據條件去除不可能的狀態,將狼羊、羊菜單獨在河一邊的情況去除,
合適情況10種分別是:
(mwgc|)
(mgc|w)、(mwc|g)、(mwg|c)
(wc|mg)、(mg|wc)
(w|mgc)、(g|mwc)、(c|mwg)
(|mwgc)
----------------------------------------------------------
3.確定鄰接關係,形成一個圖,使用鄰接矩陣存儲【也可使用鄰接鏈表】
----------------------------------------------------------
4.對圖進行遍歷,從(mwgc|)出發,用深度優先搜索找到(|mwgc)則爲一個可行的方案
【也可使用廣度優先搜索】
*/

#include<iostream>
#include<fstream>
#include<malloc.h>
using namespace std;

#define datatype char
#define ALLSTATE 1
#define SELECTSTATE 2
#define MAXSIZE 10
#define TOLEFT 0
#define TORIGHT 1

//數據結構說明
//記錄所有的情況
//使用鏈表是爲了後面將錯誤的狀態刪除比較方便
typedef struct node 
{
    int num; //用來標識情況的序號
    datatype state[10]; //用來說明情況
    bool IsRight; //用來指示該情況是否符合條件
    struct node *next; //指向下一個元素
}MyState;

typedef struct
{
    char vertex[MAXSIZE][MAXSIZE]; //頂點
    int StateGraph[MAXSIZE][MAXSIZE]; //鄰接矩陣
}MyGraph;

//全局變量聲明
int num_situation = 0; //統計情況的個數
MyState *AllState = (MyState*)malloc(sizeof(MyState)); //創建一個頭指針
MyState *pEnd = AllState; //用來指示鏈尾部
datatype way[] = { 'm','w','g','c' }; //操作的數組
ofstream out("situation.txt"); //用於將所有可能寫入文件
ifstream in("situation.txt"); //用於從文件中讀取所有可能
MyGraph *g = (MyGraph*)malloc(sizeof(MyGraph)); //建立圖的指針
int visited[MAXSIZE]; //用來記錄圖中一個結點是否被訪問過,值爲訪問它的結點編碼


//函數聲明
//-------------------1------------------------------------------------
void count_situation(); //計算所有可能的情況
void comb(datatype a[], int n, int m, datatype b[], const int M); //組合
void file2struct(); //將情況補充後存入結構體
void fix(char state[]); //補充情況
int output_allstate(int sel); //輸出所有情況
//-------------------2------------------------------------------------
void select_situation(); //篩選情況
bool IsRight(char temp[]); //判斷該狀態是否正確
//-------------------3------------------------------------------------
void create_graph(); //建立鄰接矩陣
void init_Graph();
void GetNextState(MyState* NowState, int Edge[]); //根據當前狀態求後繼狀態
void PassTheBrige(char temp[], char ch, int derction); //ch過河
int GetStateNum(char state[]); //獲得此狀態的編號
bool IsEqual(char array1[], char array2[]);
void output_graph(); //輸出圖
//-------------------4------------------------------------------------
void FindTheAnswer(int start, int end); //找到對應方法
void DFS(int s, int e); //找路徑
void printPath(int e); //輸出路徑                      
char* Num2State(int num); //根據編號返回對應狀態



void main()
{
    count_situation(); //第一步:計算所有狀態
    select_situation(); //第二步:篩選合法狀態
    create_graph(); //第三步:建立鄰接矩陣
    FindTheAnswer(GetStateNum("|mwgc"),GetStateNum("wmgc|")); //第四步:深度優先搜索
    getchar();
}

/*------------------------------第一步計算所有情況--------------------------------------*/
//計算所有可能的情況
void count_situation()
{
    datatype b[4];
    out << "|mwgc" << endl;

    for (int i = 1; i <= 4; i++)
    {
        comb(way, 4, i, b, i); //組合
    }
    out.close();

    file2struct(); //將文件中的情況修正後存入結構體
    //輸出全部結果,並修改狀態個數
    num_situation = output_allstate(ALLSTATE); 
}

//組合
void comb(datatype a[], int n, int m, datatype b[], const int M)
{
    for (int i = n; i >= m; i--)
    {
        b[m - 1] = i - 1;
        if (m > 1)
        {
            comb(a, i - 1, m - 1, b, M);
        }
        else //m==1
        {
            for (int j = M - 1; j >= 0; j--)
            {
                out << a[b[j]]; //寫到文件
            }
            out << "|" << endl;
        }
    }
}

//將情況補充後存入結構體
void file2struct()
{
    int number = 0;
    char state[10];
    if (in)
    {
        while (!in.eof())
        {
            //讀取一條狀態
            in >> state;
            //補充狀態
            fix(state);
            //將state保存到結構體中
            MyState *temp = (MyState*)malloc(sizeof(MyState));
            temp->num = number++;
            temp->next = NULL;
            strcpy(temp->state, state); //將補充過的狀態填入結構體
            pEnd->next = temp;
            pEnd = temp;
        }
    }
}

//補充情況
void fix(char state[])
{
    int i = 0;
    bool m = false, w = false, g = false, c = false;
    //檢查傳進來的串種缺少什麼元素
    while (state[i] != '|')
    {
        switch (state[i++])
        {
        case 'm':
            m = true;
            break;
        case 'w':
            w = true;
            break;
        case 'g':
            g = true;
            break;
        case 'c':
            c = true;
            break;
        default:
            break;
        }
    }
    //將所有缺少的元素補充到‘|’後面
    if (m == false)
        state[++i] = 'm';
    if (w == false)
        state[++i] = 'w';
    if (g == false)
        state[++i] = 'g';
    if (c == false)
        state[++i] = 'c';
}

/*------------------------------第二步判斷情況正誤--------------------------------------*/
//篩選情況
void select_situation()
{
    MyState *temp = AllState;
    int i = -1;
    while (temp->next != NULL)
    {
        if (!IsRight(temp->next->state)) //如果該狀態不正確
        {
            //將改狀態刪除
            MyState *del = temp->next;
            temp->next = del->next;
            free(del);
        }
        else
        {
            temp->num = i++;
            temp = temp->next;
        }

    }
    //輸出篩選過的結果,並更新狀態個數
    num_situation = output_allstate(SELECTSTATE); 
}

//判斷該狀態是否正確
bool IsRight(char temp[])
{
    int i = 0;
    bool m = false, w = false, g = false, c = false;
    while (temp[i] != '|' && temp[i] != '\0')
    {
        switch (temp[i++])
        {
        case 'm':
            m = true;
            break;
        case 'w':
            w = true;
            break;
        case 'g':
            g = true;
            break;
        case 'c':
            c = true;
            break;
        default:
            break;
        }
    }
    //wgc|m
    if (w == true && g == true && c == true && m == false)
        return false;
    //m|wgc
    if (m == true && w == false && g == false && c == false)
        return false;
    //gc|mw
    if (g == true && c == true && m == false && w == false)
        return false;
    //mw|gc
    if (m == true && w == true && g == false && c == false)
        return false;
    //wg|mc
    if (w == true && g == true && m == false && c == false)
        return false;
    //mc|wg
    if (m == true && c == true && w == false && g == false)
        return false;

        return true;

}

//輸出函數
int output_allstate(int sel)
{
    int i = 1;
    MyState *temp = AllState->next;
    if (sel == ALLSTATE)
        cout << "-------------所有的情況----------------" << endl;
    else if (sel == SELECTSTATE)
        cout << "-------------篩選後情況----------------" << endl;
    while (temp->next != NULL)
    {
        cout << "第" << i++ << "種情況:" << temp->state << endl;
        temp = temp->next;
    }
    return i - 1; //返回當前狀態的個數
}

/*------------------------------第三步建立鄰接鏈表--------------------------------------*/
//建立鄰接矩陣
/*
編程思路:
1.根據狀態的個數建造一個鄰接矩陣
2.當狀態下一個指針不空的時候,對每一個狀態有如下3~6的操作
3.求得改狀態之後的所有可能狀態
4.對所有的可能狀態進行判斷排除可能狀態中不滿足條件的
5.將滿足條件的下一個狀態與當前狀態連邊
6.下一個狀態爲當前狀態

當狀態下一個指向爲空時圖生成完畢
*/

void create_graph()
{
    init_Graph();
    int Edge[4] = { -1,-1,-1,-1 }; //記錄應該鏈接的邊
    MyState *temp = AllState->next;
    int i = 0;
    //遍歷狀態鏈表
    while (temp->next != NULL)
    {
        //頂點操作
        //給各個頂點賦初值
        strcpy(g->vertex[i++], temp->state);

        //邊操作
        //求當前狀態每一種滿足條件的後繼狀態
        GetNextState(temp,Edge);
        //對每一個後記狀態進行邊添加
        for (int j = 0; j < 4; j++)
        {
            if (Edge[j] != -1) //要加邊
            {
                g->StateGraph[temp->num][Edge[j]] = 111;
            }   
        }
        //恢復Edge
        for (int j = 0; j < 4; j++)
        {
            Edge[j] = -1;
        }
        temp = temp->next;
    }
    //輸出圖的鄰接矩陣
    output_graph();
}

//初始化圖
void init_Graph()
{
    for (int i = 0; i < 10; i++)
    {
        for (int j = 0; j < 10; j++)
        {
            g->StateGraph[i][j] = 0;
        }
    }
}

//根據當前狀態求後繼狀態
void GetNextState(MyState* NowState, int Edge[])
{
    //判斷當前狀態中人的位置
    int i_Edge = 0;
    int i = 0;
    bool m = false, w = false, g = false, c = false;

    //保存NowState
    char Copy[10];
    strcpy(Copy, NowState->state);

    while (NowState->state[i] != '|' && NowState->state[i] != '\0')
    {
        switch (NowState->state[i++])
        {
        case 'm':
            m = true;
            break;
        case 'w':
            w = true;
            break;
        case 'g':
            g = true;
            break;
        case 'c':
            c = true;
            break;
        default:
            break;
        }
    }
    //人在河左
    if (m == true)
    {
        //狼在河左,帶狼過
        if (w == true)
        {
            PassTheBrige(Copy, 'w', TORIGHT);
            PassTheBrige(Copy, 'm', TORIGHT);
            if (IsRight(Copy))
            {
                Edge[i_Edge++] = GetStateNum(Copy);
            }
            strcpy(Copy, NowState->state);
        }
        //羊在河左,帶羊過
        if (g == true)
        {
            PassTheBrige(Copy, 'g', TORIGHT);
            PassTheBrige(Copy, 'm', TORIGHT);
            if (IsRight(Copy))
            {
                Edge[i_Edge++] = GetStateNum(Copy);
            }
            strcpy(Copy, NowState->state);
        }
        //菜在河左,帶菜過
        if (c == true)
        {
            PassTheBrige(Copy, 'c', TORIGHT);
            PassTheBrige(Copy, 'm', TORIGHT);
            if (IsRight(Copy))
            {
                Edge[i_Edge++] = GetStateNum(Copy);
            }
            strcpy(Copy, NowState->state);
        }
        //人自己過河
        PassTheBrige(Copy, 'm', TORIGHT);
        if (IsRight(Copy))
        {
            Edge[i_Edge++] = GetStateNum(Copy);
        }
        strcpy(Copy, NowState->state);
    }
    else //人在河右
    {
        //狼在河右,帶狼過
        if (w == false)
        {
            PassTheBrige(Copy, 'w', TOLEFT);
            PassTheBrige(Copy, 'm', TOLEFT);
            if (IsRight(Copy))
            {
                Edge[i_Edge++] = GetStateNum(Copy);
            }
            strcpy(Copy, NowState->state);
        }
        //羊在河左,帶羊過
        if (g == false)
        {
            PassTheBrige(Copy, 'g', TOLEFT);
            PassTheBrige(Copy, 'm', TOLEFT);
            if (IsRight(Copy))
            {
                Edge[i_Edge++] = GetStateNum(Copy);
            }
            strcpy(Copy, NowState->state);
        }
        //菜在河左,帶菜過
        if (c == false)
        {
            PassTheBrige(Copy, 'c', TOLEFT);
            PassTheBrige(Copy, 'm', TOLEFT);
            if (IsRight(Copy))
            {
                Edge[i_Edge++] = GetStateNum(Copy);
            }
            strcpy(Copy, NowState->state);
        }
        //人自己過河
        PassTheBrige(Copy, 'm', TOLEFT);
        if (IsRight(Copy))
        {
            Edge[i_Edge++] = GetStateNum(Copy);
        }
        strcpy(Copy, NowState->state);
    }
}

//過河函數,將temp數組中的字母ch移動到‘|’對岸
void PassTheBrige(char temp[], char ch, int derction)
{
    int i = 0, j = 0;
    //找ch的位置
    for (i = 0; i < 5; i++)
    {
        if (temp[i] == ch)
            break;
    }
    //將ch刪除
    for (j = i; j < 5; j++)
    {
        temp[j] = temp[j + 1];
    }
    //找河的位置
    for (i = 0; i < 5; i++)
    {
        if (temp[i] == '|')
            break;
    }
    if (derction == TORIGHT)
    {
        temp[4] = ch; //給最後一個
    }
    else //過到河左
    {
        for (j = 4; j >= i; j--)
        {
            temp[j + 1] = temp[j];
        }
        temp[i] = ch;
    }
}

//獲得此狀態的num編號
int GetStateNum(char state[])
{
    MyState* temp = AllState->next;
    while (temp->next != NULL)
    {
        if (IsEqual(temp->state,state))
        {
            return temp->num;
        }
        else
        {
            temp = temp->next;
        }
    }
    return -1;
}

//判斷兩個狀態是否相等
bool IsEqual(char array1[], char array2[])
{
    int i = 0;
    bool m1 = false, w1 = false, g1 = false, c1 = false;
    bool m2 = false, w2 = false, g2 = false, c2 = false;
    while (array1[i] != '|' && array1[i] != '\0')
    {
        switch (array1[i++])
        {
        case 'm':
            m1 = true;
            break;
        case 'w':
            w1 = true;
            break;
        case 'g':
            g1 = true;
            break;
        case 'c':
            c1 = true;
            break;
        default:
            break;
        }
    }
    i = 0;
    while (array2[i] != '|' && array2[i] != '\0')
    {
        switch (array2[i++])
        {
        case 'm':
            m2 = true;
            break;
        case 'w':
            w2 = true;
            break;
        case 'g':
            g2 = true;
            break;
        case 'c':
            c2 = true;
            break;
        default:
            break;
        }
    }
    if (m1 == m2 && w1 == w2 && g1 == g2 && c1 == c2)
        return true;
    else
        return false;
}

//輸出圖
void output_graph()
{
    int i = 0, j = 0;
    cout << endl;
    cout << "-------------鄰接矩陣爲----------------" << endl;
    cout << "   ";
    for (i = 0; i < 10; i++)
    {
        cout << g->vertex[i] << "   ";
    }
    cout << endl;
    for (i = 0; i < 10; i++)
    {
        cout << g->vertex[i] << "   ";
        for (j = 0; j < 10; j++)
        {
            cout << g->StateGraph[i][j]<<"  ";
        }
        cout << endl;
        cout << endl;
    }
    cout << endl;
}

/*------------------------------第四步進行深度搜索--------------------------------------*/
//找路徑
void FindTheAnswer(int start, int end)
{
    //初始化
    for (int i = 0; i < MAXSIZE; i++)
    {
        visited[i] = -1;
    }
    visited[start] = start;
    cout << endl;
    cout << "-------------最終結果爲----------------" << endl;
    DFS(start, end);
}

//深度優先搜索
void DFS(int s, int e)
{
    if (s == e)
    {
        printPath(e);
        cout << endl;
    }
    for (int i = 1; i < MAXSIZE; i++)
    {
        if (g->StateGraph[s][i] > 0 && visited[i] == -1) //有邊且沒有被訪問
        {
            visited[i] = s; //標誌邊被s點訪問
            DFS(i, e); 
            visited[i] = -1; //回溯後修改邊爲未被訪問
        }
    }
}

//輸出
void printPath(int e)
{
    if (visited[e] != e)
    {
        printPath(visited[e]);
        cout << "-->";
    }
    //輸出e所對應的狀態
    cout << " (" << Num2State(e) << ") ";
}

//根據編號返回對應狀態
char* Num2State(int num)
{
    MyState* temp = AllState->next;
    while (temp->next != NULL)
    {
        if (temp->num == num)
        {
            return temp->state;
        }
        else
        {
            temp = temp->next;
        }
    }
    return NULL;
}

最終的結果爲:
這裏寫圖片描述
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章