写给妹妹的编程札记 6 - 搜索实战: 单词博弈

2024-05-24 04:58

本文主要是介绍写给妹妹的编程札记 6 - 搜索实战: 单词博弈,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        最近,CSDN上的在线编程比赛中,有一道题目《单词博弈》。这道题目是一个很好的可以使用搜索来解决的例子。

题目详情
本第一次在线编程大赛由文思海辉冠名,题目如下:
甲乙两个人用一个英语单词玩游戏。两个人轮流进行,每个人每次从中删掉任意一个字母,如果剩余的字母序列是严格单调递增的(按字典序a < b < c <....<z),则这个人胜利。两个人都足够聪明(即如果有赢的方案,都不会选输的方案 ),甲先开始,问他能赢么?
输入: 一连串英文小写字母,长度不超过15,保证最开始的状态不是一个严格单增的序列。
输出:1表示甲可以赢,0表示甲不能赢。
例如: 输入 bad, 则甲可以删掉b或者a,剩余的是ad或者bd,他就赢了,输出1。
又如: 输入 aaa, 则甲只能删掉1个a,乙删掉一个a,剩余1个a,乙获胜,输出0。

        有别于以往的搜索问题,在这个问题中,有两个角色,为游戏双方 - 甲和乙。 但,需要说明的是, 这个题目比平常的围棋象棋简单很多,一个是问题的搜索空间很小, 另一个是任意一个状态对甲乙来说是一样的。比如,甲乙面对字符串bad的输赢情况是一样的。 不难看出,对于一个状态,要么先玩的选手赢,要么先玩的选手输,只有这两种可能。题目已经假设最开始的状态不是一个严格单增的序列,并且如果一个选手通过删除掉一个字母到达一个单增序列的话就可以胜利。那么,我们有下面的发现:

        1. 如果一个字母序列X是单增的, 那么,对于这个序列来说, 先行者输

        2. 否则,一个长度为k的字母序列X, 删除一个字母后, 可以得到k个字母序列(删除其中的第1,2,...,k个字母分别得到字母序列Y_1, Y_2, ..., Y_k )

            a. 如果这k个字母序列中任意一个是先行者输, 那么X是先行者赢

                为什么? 假设Y_i是先行者输,那么对于字母序列X来说, 作为先行者,我们只要删除字母i,到达Y_i, 下一个选手就输了。

            b. 如果这k个字母序列中任意一个都不是先行者输, 也就是说所有k个字母序列都是先行者赢, 那么X是先行者输

                为什么? 因为对于X这个字母序列来说, 不管我们删除哪个字母,到达的k个字母序列都是先行者赢。 也就是下一个选手会赢。


        有了上面的这些观察, 让我们来看看整棵博弈搜索树是怎样的? 拿“bdca”为例吧 (图中W表示先行者赢, L表示先行者输)


        对于字母序列"bdca",首先搜索第一个分支"_dca",当搜索完分支"_dca"之后,发现"_dca"是一个先行者输的状态,也就是说我们如果删除字母b,到达字母序列"_dca"的话,下一个选手不管怎么走都是输,也就是说"bdca"是先行者赢的,我们的取胜策略是删除第一个字母。 后面的三个分支没有必要计算了。

        到这里为止,应该不难写出一个搜索程序来了。需要注意的地方是, 像往常一样, 搜索树上有很多节点是重复的,比如上面"___a", "__c_"等。 搜索时候如何避免这些重复状态将直接影响整个程序的效率。

        考虑下,搜索树中可能的字母序列数目是多少呢? 如果输入字母序列长度为k, 那么可能的字母序列为2^k  (序列中每个字母都可能被删除了或者还没有被删除)。 题目中限制输入的字母序列长度不超过15,所以搜索树中可能的字母序列不超过2^15, 是一个很小的数目。比如,"bdca"可以用15表示 (2^4 - 1), 4个bit, 每个bit都是1. “_dca”可以用14表示,除了第一个bit其他bit都是1. 我们可以申请一个空间v[], v[i]记录i对应的字母序列是先行者赢还是先行者输。 搜索的伪代码如下:

char search(int state)
{// 检查state是否已经计算过,避免重复计算if (v[state] != '') {return v[state];}// 检查state是否是单增序列, 如果是单增序列,返回"L"if (单增序列) {return 'L';}// 尝试删除每一个字母,看是否能找到先行者输的字母序列for (int i = 0; i < k; i++) {next_state = 删除字母i之后得到的新的字母序列if (search(next_state) == 'L') return 'W';}return 'L';
}

        由于搜索的整个空间很少, 下面是一个遍历所有可能字母序列的程序, 没有按上面伪代码这样编写常规的递归搜索程序。 一个不足的地方在于,可能计算了一些字母序列,但这些字母序列根本没有必要计算。 比如上图中,经过剪枝, 需要计算的字母序列数目非常少。好处当然是图方便编写简单。 至于,为什么从小到大开始计算呢? 因为每一个字母序列x,删除其中任意一个字母后得到新的字母序列y, 那么x对应的整数一定比y对应的整数大。 从小到大遍历一遍所有可能的字母序列可以保证在计算x对应的字母序列时,该字母序列通过删除字母得到的字母序列肯定都已经计算过了。

int who(const char* word)
{int   n = strlen(word);int   nState = (1<<n);int   k, nextK, i;char* v = (char*)malloc(sizeof(char) * nState);memset(v, 0, sizeof(char) * nState);for(k = 1; k < nState; k++) {// 检查是否单增字母序列int isFinished = 1;char prevChar = 'a' - 1;for(i = 0; i < n; i++) {if ((k & (1<<i)) > 0) {if (word[i] <= prevChar) {isFinished = 0; break;} else {prevChar = word[i];}}}// 1. 如果单增序列 - 先行者输if (isFinished) {v[k] = 'L';}else{// 2. 如果不是单增序列,检查可能的子节点,当找到先行者输节点时可以提前终止int canWin = 0;for(i = 0; i < n; i++) {if ((k & (1<<i)) > 0) {nextK = k ^ (1<<i);if (v[nextK] == 'L') { canWin = 1; break;}}}if (canWin) v[k] = 'W'; else v[k] = 'L';}}int win = (v[nState - 1] == 'W') ? 1 : 0;free(v);if (win) return 1; else return 0;
}


这篇关于写给妹妹的编程札记 6 - 搜索实战: 单词博弈的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/997441

相关文章

Python实现Word转PDF全攻略(从入门到实战)

《Python实现Word转PDF全攻略(从入门到实战)》在数字化办公场景中,Word文档的跨平台兼容性始终是个难题,而PDF格式凭借所见即所得的特性,已成为文档分发和归档的标准格式,下面小编就来和大... 目录一、为什么需要python处理Word转PDF?二、主流转换方案对比三、五套实战方案详解方案1:

C# async await 异步编程实现机制详解

《C#asyncawait异步编程实现机制详解》async/await是C#5.0引入的语法糖,它基于**状态机(StateMachine)**模式实现,将异步方法转换为编译器生成的状态机类,本... 目录一、async/await 异步编程实现机制1.1 核心概念1.2 编译器转换过程1.3 关键组件解析

SpringBoot实现RSA+AES自动接口解密的实战指南

《SpringBoot实现RSA+AES自动接口解密的实战指南》在当今数据泄露频发的网络环境中,接口安全已成为开发者不可忽视的核心议题,RSA+AES混合加密方案因其安全性高、性能优越而被广泛采用,本... 目录一、项目依赖与环境准备1.1 Maven依赖配置1.2 密钥生成与配置二、加密工具类实现2.1

Nginx进行平滑升级的实战指南(不中断服务版本更新)

《Nginx进行平滑升级的实战指南(不中断服务版本更新)》Nginx的平滑升级(也称为热升级)是一种在不停止服务的情况下更新Nginx版本或添加模块的方法,这种升级方式确保了服务的高可用性,避免了因升... 目录一.下载并编译新版Nginx1.下载解压2.编译二.替换可执行文件,并平滑升级1.替换可执行文件

精选20个好玩又实用的的Python实战项目(有图文代码)

《精选20个好玩又实用的的Python实战项目(有图文代码)》文章介绍了20个实用Python项目,涵盖游戏开发、工具应用、图像处理、机器学习等,使用Tkinter、PIL、OpenCV、Kivy等库... 目录① 猜字游戏② 闹钟③ 骰子模拟器④ 二维码⑤ 语言检测⑥ 加密和解密⑦ URL缩短⑧ 音乐播放

SQL Server跟踪自动统计信息更新实战指南

《SQLServer跟踪自动统计信息更新实战指南》本文详解SQLServer自动统计信息更新的跟踪方法,推荐使用扩展事件实时捕获更新操作及详细信息,同时结合系统视图快速检查统计信息状态,重点强调修... 目录SQL Server 如何跟踪自动统计信息更新:深入解析与实战指南 核心跟踪方法1️⃣ 利用系统目录

java中pdf模版填充表单踩坑实战记录(itextPdf、openPdf、pdfbox)

《java中pdf模版填充表单踩坑实战记录(itextPdf、openPdf、pdfbox)》:本文主要介绍java中pdf模版填充表单踩坑的相关资料,OpenPDF、iText、PDFBox是三... 目录准备Pdf模版方法1:itextpdf7填充表单(1)加入依赖(2)代码(3)遇到的问题方法2:pd

PyTorch中的词嵌入层(nn.Embedding)详解与实战应用示例

《PyTorch中的词嵌入层(nn.Embedding)详解与实战应用示例》词嵌入解决NLP维度灾难,捕捉语义关系,PyTorch的nn.Embedding模块提供灵活实现,支持参数配置、预训练及变长... 目录一、词嵌入(Word Embedding)简介为什么需要词嵌入?二、PyTorch中的nn.Em

在IntelliJ IDEA中高效运行与调试Spring Boot项目的实战步骤

《在IntelliJIDEA中高效运行与调试SpringBoot项目的实战步骤》本章详解SpringBoot项目导入IntelliJIDEA的流程,教授运行与调试技巧,包括断点设置与变量查看,奠定... 目录引言:为良驹配上好鞍一、为何选择IntelliJ IDEA?二、实战:导入并运行你的第一个项目步骤1

Spring Boot3.0新特性全面解析与应用实战

《SpringBoot3.0新特性全面解析与应用实战》SpringBoot3.0作为Spring生态系统的一个重要里程碑,带来了众多令人兴奋的新特性和改进,本文将深入解析SpringBoot3.0的... 目录核心变化概览Java版本要求提升迁移至Jakarta EE重要新特性详解1. Native Ima