修道士和野人过河问题 A*算法 人工智能

本文介绍了一个经典的修道士与野人过河问题,并使用A*算法寻找最优解。通过构造估价函数,实现自动搜索所有可能的渡河方案,并确保修道士的安全。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/**
*   2014-08-25 by Liy
*   修道士和野人过河问题,一共有3个修道士和3个野人,1条船
*   1、船最多可乘坐2人
*   2、两岸边 野人的数量不能多于修道士的数量,否则修道士会被吃掉
*
*   解法:A*算法, 构造估价函数
*
**/

#include <cstdio>
#include <queue>
#include <cstring>

using namespace std;

bool Safe;
int M, C, K, B;
int st[100][100][2]; // 标记状态
int add[][2] = {1,1, 0,2, 2,0, 1,0, 0,1}; //操作规则

struct Node {
    int pid; //父亲编号
    int id; // 编号,记录路径
    int m;  // 左边修道士数量
    int c;  // 左边野人数量
    int b;  // 左边船只数量
    Node() {}
    Node(int _m, int _c, int _b, int _id, int _pid = -1) {
        m = _m; c = _c; b = _b; id = _id; pid = _pid;
    }
};

struct AstarNode {
    int f; // 估计值
    int g; // 当前已划船次数
    Node node;
    AstarNode() {}
    AstarNode(int _g, Node _node) {
        node = _node;
        g = _g;
        f = g + h();
    }
    bool operator < (const AstarNode& a) const {
        return f > a.f;
    }
    int h() { //启发函数,从当前状态到达最终状态需要的最少次数
        return node.m + node.c - K * node.b;
    }
};

// 判断状态是否合法
bool OK(int m, int c, int b) {
    if(m > M || m < 0 || c > C || c < 0 || (m != 0 && m < c) || (M-m != 0 && (M-m) < (C-c))) return false;
    if(st[m][c][b] != -1) return false;
    return true;
}

vector<Node> PathNode;
priority_queue<AstarNode> Q;

void printPath(int id) {
    if(id != -1) {
        printPath(PathNode[id].pid);
        if(PathNode[id].pid == -1) printf("起始状态: ");
        else if(PathNode[id].m == 0 && PathNode[id].c == 0) printf("终止状态: ");
        else printf("    ----> ");
        printf("(%d, %d, %d)\n", PathNode[id].m, PathNode[id].c, PathNode[id].b);
    }
}

void Astar() {
    while(!Q.empty()) Q.pop();
    PathNode.clear();

    memset(st, -1, sizeof(st));
    PathNode.push_back(Node(M, C, B, 0, -1));
    Q.push(AstarNode(0, PathNode[0]));
    st[M][C][B] = 0;

    int m, c, b, flag, id;
    while(!Q.empty()) {
        AstarNode as = Q.top(); Q.pop();
        //printf("----%d %d %d %d %d----\n", as.f, as.g, as.node.m, as.node.c, as.node.b);
        if(as.node.m == 0 && as.node.c == 0) {
            printf("渡河方案如下:\n");
            printf("          (M, C, B)\n");
            printPath(as.node.id);
            printf("可以安全渡河,来回最少需 %d 次便可!\n", as.g);
            Safe = true;
            return;
        }

        if(as.node.b == 1) flag = -1;
        else flag = 1;
        b = 1 - as.node.b;
        id = PathNode.size() - 1;

        for(int i = 0; i < 5; ++i) {
            m = as.node.m + flag * add[i][0];
            c = as.node.c + flag * add[i][1];

            if(OK(m, c, b)) {
                ++id;
                st[m][c][b] = as.g + 1;
                PathNode.push_back(Node(m, c, b, id, as.node.id));
                Q.push(AstarNode(as.g + 1, PathNode[id]));
            }
        }
    }
}

int main(int argc, char *argv[]) {
    for(int i = 3; i < 6; ++i){
        M = C = i; B = 1; K = 2;
        printf("M(修道士) = %d 人, C(野人) = %d 人, K(限乘) = %d 人, B(船) = %d条.\n", M, C, K, B);
        Safe = false; Astar();
        if(!Safe) {
            printf("没有方案可以安全渡河!!!\n");
        }
        putchar(10);
    }
    return 0;
}


// 这里只打印出了一种方案,也可以打印出所有的最优可行方案, 这里船只能有 1 条,且只能坐 2 个人,没有考虑其它的情况, 修道士和野人的人数可以改变

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值