农夫过河问题

. 3 min read

农夫过河问题是上学期数据结构作业之一。我的C语言还处于业余水平,算法和数据结构更是马马虎虎,转专业之前也没做过类似的项目,网上有各种算法和分析,知乎“郑启威”的回答比较清晰,代码写起来也干净利落,花了一晚上消化了基本思路,又自己写了一遍完整的实现。全部使用C语言写的,C++什么的还待学习。

关键点有两个:

不满意的方面:

在编写过程中尽量避免全局变量,函数之间实现松耦合,然而在结果的输出上并不优雅,将路径保存在了临时数组中,在findPath递归调用过程中就输出了结果,而不是将所有路径转存储然后统一输出。还请看官不吝赐教。

以下是完整代码:

/*农夫过河问题求解*/
#include <stdio.h>
#define NUM_NODES       16
#define TRUE            1
#define FALSE           0
/*二进制位表示左岸人、狼、羊、菜的状态*/
#define HUMAN_BIT       8
#define WOLF_BIT        4
#define SHEEP_BIT       2
#define CABBAGE_BIT     1
/*判断人、狼、羊、菜的状态*/
#define HUMAN(state)    ((state) & HUMAN_BIT)
#define WOLF(state)     ((state) & WOLF_BIT)
#define SHEEP(state)    ((state) & SHEEP_BIT)
#define CABBAGE(state)  ((state) & CABBAGE_BIT)

int main()
{
    void createStateMatrix(int matrix[][NUM_NODES]);
    void removeInvalidState(int matrix[][NUM_NODES]);
    void findPath(int graph[][NUM_NODES], int startNode, int endNode, int depth);
    int matrix[NUM_NODES][NUM_NODES] = { 0 };
    printf("农夫过河问题, 所有方案如下:\n");
    createStateMatrix(matrix);      //构建矩阵
    removeInvalidState(matrix);     //移除非法状态
    findPath(matrix, 15, 0, 0);     //深度优先搜索,显示结果
    return 0;
}
/*构造矩阵,如果两个状态可以互相转换,将矩阵对应的位置设为1。*/
void createStateMatrix(int matrix[][NUM_NODES])
{
    for (int i = 0, j; i < HUMAN_BIT; i++) {
        j = i | HUMAN_BIT;
        matrix[i][j] = matrix[j][i] = 1;
        if (!WOLF(i))
            matrix[i][j | WOLF_BIT] = matrix[j | WOLF_BIT][i] = 1;
        if (!SHEEP(i))
            matrix[i][j | SHEEP_BIT] = matrix[j | SHEEP_BIT][i] = 1;
        if (!CABBAGE(i))
            matrix[i][j | CABBAGE_BIT] = matrix[j | CABBAGE_BIT][i] = 1;
    }
}
/*移除单个非法状态*/
void removeOneInvalidState(int matrix[][NUM_NODES], int state)
{
    for (int i = 0; i < NUM_NODES; ++i){
        matrix[i][state] = matrix[state][i] = 0;
        matrix[i][15 & ~state] = matrix[15 & ~state][i] = 0;
    }
}
/*判断并移除所有非法状态*/
void removeInvalidState(int matrix[][NUM_NODES])
{
    for (int i = 0; i < HUMAN_BIT; ++i)             //只考虑人不在的状态
        if ((WOLF(i) && SHEEP(i)) || (SHEEP(i) && CABBAGE(i))) 
            removeOneInvalidState(matrix, i);
}
/*对图进行深度优先遍历,获取所有1111->0000的路径*/
void findPath(int matrix[][NUM_NODES], int currentNode, int targetNode, int depth)
{
    static int visited[NUM_NODES];     //访问标记
    static int path[NUM_NODES];        //记录路径
    void printResult(int path[], int depth, int count);
    path[depth++] = currentNode;
    if (currentNode == targetNode) {
        static int count = 0;
        count++;                        //计数
        printResult(path, depth, count);//输出结果
    }
    else {
        visited[currentNode] = TRUE;
        for (int j = 0; j < NUM_NODES; ++j)
            if (matrix[currentNode][j] && !visited[j])
                findPath(matrix, j, targetNode, depth);
        visited[currentNode] = FALSE;
        depth--;
    }
}
/*输出结果*/
void printResult(int path[], int depth, int count)
{
    int state, nextState;
    printf("方案%d:农夫", count);
    for (int i = 0; i < depth - 1; ++i) {
        state = path[i];
        nextState = path[i + 1];
        switch (state ^ nextState) {
        case HUMAN_BIT:                 printf("单独"); break;
        case HUMAN_BIT | CABBAGE_BIT:   printf("带菜"); break;
        case HUMAN_BIT | SHEEP_BIT:     printf("带羊"); break;
        case HUMAN_BIT | WOLF_BIT:      printf("带狼"); break;
        default:printf("异常:%d->%d", state, nextState); break;
        }
        printf("%s->", HUMAN(state) ? "过去" : "返回");
    }
    printf("完成!\n");
}