3. 博弈树 (15分)

下棋属于一种博弈游戏,博弈过程可以用树(博弈树)来表示。假设游戏由两个人( A 和 B )玩,开始由某个人从根结点开始走,两个人轮流走棋,每次只能走一步, 下一步棋只能选择当前结点的孩子结点,谁先走到叶子结点为胜。例如,对于下图所示的博弈树,若 A 先走,可以选 f , B 若选 h ,则 A 选 j 胜。

编写一程序,让计算机和人下棋。当计算机走下一步时,可以根据以下情况决定下一步:

(1) 若存在可以确保取胜的一个孩子结点,则选择该结点作为下一步;

(2) 若存在多个可以确保取胜的孩子结点,则选择其中高度最小的结点作为下一步(若有多个选择,则选最左边的结点);

(3) 若不存在可以确保取胜的一个孩子结点,则选择高度最大的孩子结点作为下一步(若有多个选择,则选最左边的结点);

例: (下面的黑体为输入)

(a,(b,(x)),(c,(d),(e,(g),(h)),(f)))

a

b

x

c

d

e

g

h

f

Who play first(0: computer; 1: player )?

1

player:

c

computer: d

Sorry, you lost.

Continue(y/n)?

y

Who play first(0: computer; 1: player )?

1

player:

x

illegal move.

player:

b

computer: x

Sorry, you lost.

Continue(y/n)?

y

Who play first(0: computer; 1: player )?

0

computer: c

player:

f

Congratulate, you win.

Continue(y/n)?

n

测试输入期待的输出时间限制内存限制额外进程
测试用例 1以文本方式显示
  1. (a,(b,(x)),(c,(d),(e,(g),(h)),(f)))↵
  2. 1↵
  3. c↵
  4. y↵
  5. 1↵
  6. x↵
  7. b↵
  8. y↵
  9. 0↵
  10. f↵
  11. n↵
以文本方式显示
  1. a↵
  2. b↵
  3. x↵
  4. c↵
  5. d↵
  6. e↵
  7. g↵
  8. h↵
  9. f↵
  10. Who play first(0: computer; 1: player )?↵
  11. player:↵
  12. computer: d↵
  13. Sorry, you lost.↵
  14. Continue(y/n)?↵
  15. Who play first(0: computer; 1: player )?↵
  16. player:↵
  17. illegal move.↵
  18. player:↵
  19. computer: x↵
  20. Sorry, you lost.↵
  21. Continue(y/n)?↵
  22. Who play first(0: computer; 1: player )?↵
  23. computer: c↵
  24. player:↵
  25. Congratulate, you win.↵
  26. Continue(y/n)?↵
1秒64M0
测试用例 2以文本方式显示
  1. (a,(b,(c,(d),(e)),(f)),(g,(h),(i)),(j,(k,(m),(n),(o),(p,(r))),(x,(y,(z)))))↵
  2. 1↵
  3. j↵
  4. y↵
  5. y↵
  6. 1↵
  7. b↵
  8. y↵
  9. 0↵
  10. k↵
  11. n↵
以文本方式显示
  1. a↵
  2. b↵
  3. c↵
  4. d↵
  5. e↵
  6. f↵
  7. g↵
  8. h↵
  9. i↵
  10. j↵
  11. k↵
  12. m↵
  13. n↵
  14. o↵
  15. p↵
  16. r↵
  17. x↵
  18. y↵
  19. z↵
  20. Who play first(0: computer; 1: player )?↵
  21. player:↵
  22. computer: x↵
  23. player:↵
  24. computer: z↵
  25. Sorry, you lost.↵
  26. Continue(y/n)?↵
  27. Who play first(0: computer; 1: player )?↵
  28. player:↵
  29. computer: f↵
  30. Sorry, you lost.↵
  31. Continue(y/n)?↵
  32. Who play first(0: computer; 1: player )?↵
  33. computer: j↵
  34. player:↵
  35. computer: m↵
  36. Sorry, you lost.↵
  37. Continue(y/n)?↵
1秒64M0
测试用例 3以文本方式显示
  1. (a,(b,(c,(d),(e)),(f)),(g,(h),(i)),(q,(k,(m),(n),(o),(p,(r))),(x,(y,(z)))))↵
  2. 1↵
  3. q↵
  4. x↵
  5. y↵
  6. n↵
以文本方式显示
  1. a↵
  2. b↵
  3. c↵
  4. d↵
  5. e↵
  6. f↵
  7. g↵
  8. h↵
  9. i↵
  10. q↵
  11. k↵
  12. m↵
  13. n↵
  14. o↵
  15. p↵
  16. r↵
  17. x↵
  18. y↵
  19. z↵
  20. Who play first(0: computer; 1: player )?↵
  21. player:↵
  22. computer: x↵
  23. player:↵
  24. illegal move.↵
  25. player:↵
  26. computer: z↵
  27. Sorry, you lost.↵
  28. Continue(y/n)?↵
1秒64M0

博弈树学习笔记:

完全博弈问题--下棋

特点:

    双人对弈: 轮流下,一人走一步.

    信息完备: 双方看到的信息一样.

    零和: 双方利益冲突,对一方有利则对另一方不利。

极小极大搜索方法: (假定对手每次回应都正确,即选择MIN)

    假定有一个评价函数可以对所有的棋局进行评估.

    评价函数值大于0,棋局对我方有利,对对方不利,反之同理.

    对节点N取估价函数f(N),分两类节点:

        Max,有选择时取最大;

        Min,有选择时取最小.

具体思路:

    1.我方走棋时,首先按照一定的搜索深度生成出给定深度d以内的所有状态,计算所有叶节点的评价函数值, 然后从d-1层节点开始逆向计算.

    2.对于我方要走的节点(MAX),取其子节点中的最大值为该节点的值(选择对我方有利的棋)。

    3.对于对方要走的节点(MIN),取其子节点中的最小值为该节点的值(选择对我方不利的棋)。

    4.直到计算出根节点的值为止,获得根节点取值的分枝即为最佳选择.

    5.对方回应一步棋后,重新演算.

显然这样做太低效了,需要剪枝,分析:

α-β搜索方法定义:(其实就是给节点定义范围区间--[α,β],初始化为+-∞)

    Max节点的下界为α,Min节点的上界为β.

    极大节点N,从一个子树获得的α值和β值,可以传送给其他子节点

    极大节点N的α值只可能越改越大,极小节点M的β值只可能越改越小.

    从单边子节点往父节点推,极大值父节点只更改α值,极小值父节点只更改β值.

α剪枝(发生在极小层节点)---从一个子树获得的极大节点的α值,可以传送给该节点的其他子树,从而选择α剪枝机会

    若对任一极小值层节点,α(任意父辈层)≥β(后继层),则可中止此MIN节点以下的搜索过程.

    此MIN节点的最终倒推值就确定为此β值.

β剪枝(发生在极大层节点)---从一个子树获得的极小节点的β值,可以传送给该节点的其他子节点,从而选择β剪枝机会

    若对任一极大值层节点,α(后继层)≥β(任意父辈层),则可中止此MAX节点以下的搜索过程.

    此MAX节点的最终倒推值就确定为此α值。

代码如下:

        吐槽:晚上写完感觉逻辑没问题终于交了,结果还是WA,后来发现是输出少了空格(这一句:Who play first(0: computer; 1: player )?);改了之后AC了,,,但是看了看报表,看到一个1378B,22行过的,于是我看着我9492B,391行的代码陷入了沉思(嘶,不是哥们,这我怎么睡的着啊?就22行,我是真想不明白,除了printf/cout还能怎么写,毕竟这次测试用例是给出的三个,没有隐藏)

        强调尽量不要格式化文档,vscode的格式化文档害我WA好多次┭┮﹏┭┮,对了,现在只有6755B 303行了(合并了一些函数,去掉了一些实际上需要但是这道题测试用例考不到的检错代码)

        吃个饭回来写注释。

#include <iostream>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
typedef enum
{
    ATOM,
    LIST
} ElemClass;
typedef struct GLNode
{
    ElemClass Class;
    union
    {
        char atom;
        struct
        {
            GLNode *head;
            GLNode *tail;
        } ptr;
    };
} *Glist;
typedef struct Node
{
    int status;
    char data;
    struct Node *first, *next;
} *Tree;
int CreateGlist(Glist &L, string s)
{
    if (s == "()")
        L = NULL;
    else
    {
        L = new GLNode;
        if (s.length() == 1)
        {
            L->Class = ATOM;
            L->atom = s[0];
        }
        else
        {
            L->Class = LIST;
            string sub, headsub;
            sub = s.substr(1, s.size() - 2);
            Glist p, q;
            p = L;
            while (!sub.empty())
            {
                int i, k = 0;
                int n = sub.length();
                char c = '\0';
                for (i = 0; i < n && (c != ',' || k != 0); i++)
                {
                    c = sub[i];
                    k += (c == '(') ? 1 : ((c == ')') ? -1 : 0);
                }
                headsub = (i < n) ? sub.substr(0, i - 1) : sub;
                sub = (i < n) ? sub.substr(i, n - i) : "";
                CreateGlist(p->ptr.head, headsub);
                q = p;
                if (!sub.empty())
                {
                    p = new GLNode;
                    p->Class = LIST;
                    q->ptr.tail = p;
                }
            }
            q->ptr.tail = NULL;
        }
    }
    return 1;
}
void CreateTree(Tree& root, Glist L)
{
    if (L)
    {
        root = new Node;
        root->data = L->ptr.head->atom;
        root->first = NULL;
        root->next = NULL;
        if (L->ptr.tail)
        {
            Glist head = L->ptr.tail->ptr.head;
            Glist t = L->ptr.tail->ptr.tail;
            CreateTree(root->first, head);
            Node* p = root->first;
            while (t)
            {
                head = t->ptr.head;
                t = t->ptr.tail;
                CreateTree(p->next, head);
                p = p->next;
            }
            p->next = NULL;
        }
    }
    else
        root = NULL;
}
int PreTreat(Tree root)
{
    if (!root)
        return 1;
    if (!root->first)
        root->status = 1;
    PreTreat(root->first);
    PreTreat(root->next);
    if (root->first)
    {
        Node *p = root->first;
        int flag = 1;
        while (p)
        {
            if (p->status == 1)
            {
                flag = 0;
                break;
            }
            p = p->next;
        }
        root->status = flag;
    }
    return 1;
}
int GetHeight(Tree root)
{
    if (root == NULL)
        return 0;
    else
    {
        int head, h2;
        head = GetHeight(root->first);
        if (head != 0)
        {
            root = root->first;
            while (root)
            {
                h2 = GetHeight(root->next);
                if (head < h2)
                    head = h2;
                root = root->next;
            }
        }
        return head + 1;
    }
}
Tree Computer(Tree root)
{
    int win = INF, lose = 0;
    Tree p = root->first, q1, q2;
    while (p)
    {
        int head = GetHeight(p);
        if (p->status == 1)
        {
            if (head < win)
            {
                win = head;
                q1 = p;
            }
        }
        else
        {
            if (head > lose)
            {
                lose = head;
                q2 = p;
            }
        }
        p = p->next;
    }
    if (win != INF)
    {
        cout << "computer: " << q1->data << endl;
        return q1;
    }
    else
    {
        cout << "computer: " << q2->data << endl;
        return q2;
    }
    return root;
}
Tree Human(Tree root)
{
    cout << "player:" << endl;
    char c;
    cin.ignore();
    cin >> c;
    Node *p = root->first;
    while (p)
    {
        if (p->data == c)
            return p;
        p = p->next;
    }
    cout << "illegal move." << endl;
    return root;
}
Tree ValidMove(Tree p)
{
    while (1)
    {
        Tree q;
        q = Human(p);
        if (q != p)
            return q;
    }
}
int Move(Tree root)
{
    while (true)
    {
        cout << "Who play first(0: computer; 1: player )?" << endl;
        int player, flag = 0;
        cin >> player;
        Tree p;
        if (player == 0)
        {
            p = Computer(root);
            flag = 0;
        }
        else
        {
            p = Human(root);
            flag = 1;
        }
        if (p->first == nullptr)
        {
            if (flag == 0)
                cout << "Sorry, you lost." << endl;
            else
                cout << "Congratulate, you win." << endl;
            cout << "Continue(y/n)?" << endl;
            char c;
            cin >> c;
            if (c == 'y')
                continue;
            else
                return 1;
        }
        while (true)
        {
            if (flag == 1 && p != root)
            {
                p = Computer(p);
                if (p->first == nullptr)
                {
                    cout << "Sorry, you lost." << endl;
                    break;
                }
                p = ValidMove(p);
                if (p->first == nullptr)
                {
                    cout << "Congratulate, you win." << endl;
                    break;
                }
            }
            else
            {
                p = ValidMove(p);
                if (p->first == nullptr)
                {
                    cout << "Congratulate, you win." << endl;
                    break;
                }
                p = Computer(p);
                if (p->first == nullptr)
                {
                    cout << "Sorry, you lost." << endl;
                    break;
                }
            }
        }
        cout << "Continue(y/n)?" << endl;
        char c;
        cin >> c;
        if (c == 'y')
            continue;
        else
            return 1;
    }
    return 1;
}
int main()
{
    string s;
    cin >> s;
    int i;
    for (i = 0; s[i] != '\0'; i++)
        if (s[i] >= 'a' && s[i] <= 'z')
            cout << s[i] << endl;
    Glist L;
    CreateGlist(L, s);
    Tree root;
    CreateTree(root, L);
    PreTreat(root);
    Move(root);
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/104788.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

OTA语音芯片NV040C在智能电动牙刷的应用

以往我们对牙齿的清洁是使用的是手动方式进行&#xff0c;用柔软的牙刷刷毛去进行牙齿的清洁。但现在我们拥有了一种新颖的刷牙方式&#xff0c;靠电力去驱动、清洁我们的牙齿。电动牙刷的刷头通过快速旋转&#xff0c;产生高频振动&#xff0c;将牙膏迅速分解为细小的泡沫&…

支付宝支付接入流程

一、 接入准备 支付宝支付流程没有微信那么复杂&#xff0c;而且支付宝支持沙箱。登录支付宝开放平台控制台 点击开发工具中的沙箱 接口加密方式&#xff0c;我这里使用的是自定义密钥。生成密钥的方式 使用支付宝官方提供的密钥工具&#xff0c;唯一要注意的是支付宝密钥工具…

idea + Docker-Compose 实现自动化打包部署(仅限测试环境)

一、修改docker.service文件&#xff0c;添加监听端口 vi /usr/lib/systemd/system/docker.service ExecStart/usr/bin/dockerd -H fd:// --containerd/run/containerd/containerd.sock -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock重启docker服务 systemctl daemo…

线性代数3:矢量方程

一、前言 欢迎回到系列文章的第三篇文章&#xff0c;内容是线性代数的基础知识&#xff0c;线性代数是机器学习背后的基础数学。在我之前的文章中&#xff0c;我介绍了梯队矩阵形式。本文将介绍向量、跨度和线性组合&#xff0c;并将这些新想法与我们已经学到的内容联系起来。本…

基于 Redis + Lua 脚本实现分布式锁,确保操作的原子性

1.加锁的Lua脚本&#xff1a; lock.lua --- -1 failed --- 1 success--- getLock key local result redis.call(setnx , KEYS[1] , ARGV[1]) if result 1 then--PEXPIRE:以毫秒的形式指定过期时间redis.call(pexpire , KEYS[1] , 3600000) elseresult -1;-- 如果value相同&…

数据清洗(data clean)

整理了下数据清洗的基本流程&#xff0c;异常值部分整理在了EDA中。

什么是简单网络管理协议(SNMP)

简单网络管理协议&#xff08;SNMP&#xff1a;Simple Network Management Protocol&#xff09;是由互联网工程任务组&#xff08;IETF&#xff1a;Internet Engineering Task Force &#xff09;定义的一套网络管理协议。该协议基于简单网关监视协议&#xff08;SGMP&#xf…

React中的Virtual DOM(看这一篇就够了)

文章目录 前言了解Virtual DOMreact创建虚拟dom的方式React Element虚拟dom的流程虚拟dom和真实dom的对比后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;react合集 &#x1f431;‍&#x1f453;博主在前端领域还有很多知识和技术需要掌…

DevOps持续集成-Jenkins(3)

文章目录 DevOpsDevOps概述Jenkins实战3&#xff1a;实战1和实战2的加强版&#xff08;新增SonarQube和Harbor&#xff09;⭐环境准备⭐项目架构图对比Jenkins实战1和实战2&#xff0c;新增内容有哪些&#xff1f;SonarQube教程采用Docker安装SonarQube &#xff08;在Jenkins所…

生成树协议:监控 STP 端口和交换机

什么是生成树协议 生成树协议 &#xff08;STP&#xff09; 用于网络交换机&#xff0c;以防止循环和广播风暴。在局域网 &#xff08;LAN&#xff09; 中&#xff0c;两条或多条冗余路径可以连接到同一网段。当交换机或网桥从所有可用端口传输帧时&#xff0c;这些帧开始在网…

基于单片机设计的智能窗帘控制系统

一、前言 智能家居技术在近年来取得了巨大的发展&#xff0c;并逐渐成为人们日常生活中的一部分。智能家居系统带来了便利、舒适和高效的生活体验&#xff0c;拥有广泛的应用领域&#xff0c;其中之一就是智能窗帘控制系统。 传统窗帘需要手动操作&#xff0c;打开或关闭窗帘…

华硕天选1天选2天选3天选4天选air原厂预装出厂系统恢复安装教程方法

华硕天选1天选2天选3天选4天选air原厂预装出厂系统恢复安装教程方法 第一:自备原装swm/esd/wim/iso等格式系统文件&#xff0c;以上这几种格式文件安装恢复非常简单&#xff0c;使用PE工具即可完成恢复安装&#xff0c;还有一种安装方法就是华硕zip工厂恢复模式 1.首先需要自…

Adaptive AUTOSAR RTA-VRTE工具链介绍

ETAS Adaptive AUTOSAR RTA-VRTE是一种面向服务架构的中间件方案,提供了自适应AutoSAR平台,为应用层软件提供了运行环境. RTA-VRTE start kit的构建系统在主机VM内执行,可以创建AUTOSAR自适应应用程序并将其部署到一个或多个目标ECU虚拟机.

【VPX610】 青翼科技基于6U VPX总线架构的高性能实时信号处理平台

板卡概述 VPX610是一款基于6U VPX架构的高性能实时信号处理平台&#xff0c;该平台采用2片TI的KeyStone系列多核DSP TMS320C6678作为主处理单元&#xff0c;采用1片Xilinx的Virtex-7系列FPGA XC7VX690T作为协处理单元&#xff0c;具有2个FMC子卡接口&#xff0c;各个处理节点之…

linux-文件系统

目录 一、文件系统 1.分区 2.文件系统分类 3.文件系统创建工具 4.查看文件系统的属性 5.挂载 6.buffer和cache 一、文件系统 1.分区 1-4个主分区 第五个序号开始&#xff0c;是逻辑分区 2.文件系统分类 vfs文件系统 ------------- virtualenv file System&#xff0…

智慧社区燃气管网监测系统

燃气易燃易爆&#xff0c;一旦操作不当或疏忽大意&#xff0c;极易引发燃气安全事故&#xff0c;造成严重后果&#xff0c;2023年10月24日&#xff0c;在吉林某小区&#xff0c;发生了燃气使用不当产生的爆炸导致了1人死亡&#xff0c;1人重伤&#xff0c;15人轻伤&#xff0c;…

【嵌入式开源库】timeslice的使用,完全解耦的时间片轮询框架构

完全解耦的时间片轮询框架构 简介项目代码timeslice.htimeslice.clist.hlist.c 创建工程移植代码实验函数说明timeslice_task_inittimeslice_task_addtimeslice_tak_deltimeslice_get_task_num 结尾 简介 timeslice是一个时间片轮询框架&#xff0c;他是一个完全解耦的时间片轮…

电脑视频怎么转音频mp3

如果你在电脑上观看视频时喜欢上某个片段的背景音乐&#xff0c;且想将喜欢的背景音乐制作为手机铃声。我是建议你将此视频转换为 MP3 格式&#xff0c;因为 MP3 几乎与所有设备相兼容&#xff0c;让你可以在不同设备上不受限制地去聆听它。那该如何转换呢&#xff1f;无需担心…

LinkedList概念+MyLinkedList的实现

文章目录 LinkedList笔记一、 LinkedList1.概念2.LinkedList的构造方法3.LinkedList的遍历 二、MyLinkedList的实现1.定义内部类2.打印链表、求链表长度、判断是否包含关键字3. 头插法和尾插法4.在任意位置插入5.删除结点6.清空链表 LinkedList笔记 一、 LinkedList 1.概念 L…

将两个有序顺序表合并为一个新的有序顺序表,并由函数返回结果顺序表

将两个有序顺序表合并为一个新的有序顺序表&#xff0c;并由函数返回结果顺序表 算法思路&#xff1a; 这个其实就是一个归并排序&#xff0c;我们这里两顺序表为升序&#xff0c;要合并成一个升序表 用i和j分别标记顺序表A和顺序表B的元素&#xff0c;然后新表是C 每次从A和…