【C++动态规划 BFS 博弈】3283. 吃掉所有兵需要的最多移动次数|2473

本文涉及知识点

C++动态规划
C++BFS算法
数学 博弈

LeetCode3283. 吃掉所有兵需要的最多移动次数

给你一个 50 x 50 的国际象棋棋盘,棋盘上有 一个 马和一些兵。给你两个整数 kx 和 ky ,其中 (kx, ky) 表示马所在的位置,同时还有一个二维数组 positions ,其中 positions[i] = [xi, yi] 表示第 i 个兵在棋盘上的位置。
Alice 和 Bob 玩一个回合制游戏,Alice 先手。玩家的一次操作中,可以执行以下操作:
玩家选择一个仍然在棋盘上的兵,然后移动马,通过 最少 的 步数 吃掉这个兵。注意 ,玩家可以选择 任意 一个兵,不一定 要选择从马的位置出发 最少 移动步数的兵。
在马吃兵的过程中,马 可能 会经过一些其他兵的位置,但这些兵 不会 被吃掉。只有 选中的兵在这个回合中被吃掉。
Alice 的目标是 最大化 两名玩家的 总 移动次数,直到棋盘上不再存在兵,而 Bob 的目标是 最小化 总移动次数。
假设两名玩家都采用 最优 策略,请你返回可以达到的 最大 总移动次数。

在一次 移动 中,如下图所示,马有 8 个可以移动到的位置,每个移动位置都是沿着坐标轴的一个方向前进 2 格,然后沿着垂直的方向前进 1 格。

在这里插入图片描述

示例 1:

输入:kx = 1, ky = 1, positions = [[0,0]]

输出:4

解释:
在这里插入图片描述

马需要移动 4 步吃掉 (0, 0) 处的兵。

示例 2:

输入:kx = 0, ky = 2, positions = [[1,1],[2,2],[3,3]]

输出:8
在这里插入图片描述

解释:
Alice 选择 (2, 2) 处的兵,移动马吃掉它需要 2 步:(0, 2) -> (1, 4) -> (2, 2) 。
Bob 选择 (3, 3) 处的兵,移动马吃掉它需要 2 步:(2, 2) -> (4, 1) -> (3, 3) 。
Alice 选择 (1, 1) 处的兵,移动马吃掉它需要 4 步:(3, 3) -> (4, 1) -> (2, 2) -> (0, 3) -> (1, 1) 。
示例 3:
输入:kx = 0, ky = 0, positions = [[1,2],[2,4]]
输出:3
解释:
Alice 选择 (2, 4) 处的兵,移动马吃掉它需要 2 步:(0, 0) -> (1, 2) -> (2, 4) 。注意,(1, 2) 处的兵不会被吃掉。
Bob 选择 (1, 2) 处的兵,移动马吃掉它需要 1 步:(2, 4) -> (1, 2) 。
提示:
0 <= kx, ky <= 49
1 <= positions.length <= 15
positions[i].length == 2
0 <= positions[i][0], positions[i][1] <= 49
positions[i] 两两互不相同。
输入保证对于所有 0 <= i < positions.length ,都有 positions[i] != [kx, ky] 。

BFS +动态规划之记忆化搜索 + 博弈

注意:国际象棋没有马腿。
n = pos.length
pos2 = pos, pos追加{kx,ky}
dis[i][j] 记录pos[i]到pos[j]的最少跳跃次数。

动态规划的状态表示

dp[i][m] 记录吃光剩余兵需要的跳跃次数。马在pos2[i],(1<<i)&m表示第i个兵是否存活。为了方便,增加变量bTurn表示当前回合是否是Alice的回合。
dp[i][m] 为-1表示未0处理。

记忆化搜索的函数Rec

函数的参数:i,m,bTurn
如果m等于0,return 0。
如果dp[i][m]不是-1,return dp[i][m]。
如果bTurn ,
枚举存活的兵j
return dp[i][m] = max(dis[i][j] + Rec(j,m ^( 1 << j ),false)
如果是Bom的回合:
return dp[i][m] = min(dis[i][j] + Rec(j,m ^( 1 << j ),true)

初始调用

return Rec(n,(1<<n)-1,true)

代码

核心代码

class Solution {
		public:
			int maxMoves(int kx, int ky, vector<vector<int>>& positions) {
				const int N = positions.size();
				const int N2 = N + 1;
				vector<pair<int, int>> pos2;
				for (const auto& v : positions) {
					pos2.emplace_back(v[0], v[1]);
				}
				pos2.emplace_back(kx,ky);

				vector<vector<int>> dis(N2, vector<int>(N2));
				for (int i = 0; i < N2; i++) {
					auto dis1 = Dis(pos2[i].first, pos2[i].second);
					for (int j = 0; j < N2; j++) {
						dis[i][j] = dis1[pos2[j].first][pos2[j].second];
					}
				}
				vector<vector<int>> dp(N2, vector<int>(1 << N,-1));
				function<int(int, int, bool)> Rec = [&](int pos, int mask, bool bTurn) {
					if (0 == mask) { return 0; }
					if (-1 != dp[pos][mask]) {
						return  dp[pos][mask]	;
					}
					int a[2] = { INT_MAX / 2,INT_MIN / 2 };
					for (int i = 0; i < N; i++) {
						if (!((1 << i) & mask)) { continue; }
						if (bTurn) {
							a[bTurn] = max(a[bTurn], Rec(i, mask ^ (1 << i), !bTurn) + dis[pos][i]);
						}
						else {
							a[bTurn] = min(a[bTurn], Rec(i, mask ^ (1 << i), !bTurn) + dis[pos][i]);
						}
					}
					return dp[pos][mask]= a[bTurn];
				};
				return Rec(N,( 1 << N)-1, true);
			}
			vector<vector<int>> Dis(int x, int y) {
				const int iNotMay = 1'000'000;
				vector<vector<int>> dis(50, vector<int>(50, iNotMay));
				queue<pair<int, int>> que;
				auto Add = [&](int x, int y,int iDis) {
					if ((x < 0) || (x >= 50)) { return; }
					if ((y < 0) || (y >= 50)) { return; }
					if (iNotMay != dis[x][y]) { return; }
					dis[x][y] = iDis;
					que.emplace(x, y);
				};
				pair<int,int> moves[] = { {1,1},{1,-1},{-1,1},{-1,-1} };
				Add(x, y, 0);				
				while (que.size()) {
					const auto [x, y] = que.front();
					que.pop();	
					for (const auto& [x1, y1] : moves)
					{
						Add(x + 2*x1, y + y1, dis[x][y] + 1);
						Add(x+x1,y+ 2*y1, dis[x][y] + 1);
					}
				}
				return dis;
			}
		};

单元测试

int kx,  ky;
		vector<vector<int>> positions;
		TEST_METHOD(TestMethod11)
		{
			kx = 1, ky = 1, positions = { {0,0} };
			auto res = Solution().maxMoves(kx, ky, positions);
			AssertEx(4, res);
		}
		TEST_METHOD(TestMethod12)
		{
			kx = 0, ky = 2, positions = { {1,1},{2,2},{3,3} };
			auto res = Solution().maxMoves(kx, ky, positions);
			AssertEx(8, res);
		}
		TEST_METHOD(TestMethod13)
		{
			kx = 0, ky = 0, positions = { {1,2},{2,4} };
			auto res = Solution().maxMoves(kx, ky, positions);
			AssertEx(3, res);
		}

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

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

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

相关文章

6.824/6.5840 Lab 2: Key/Value Server

故事里能毁坏的只有风景 谁也摧毁不了我们的梦境 弦月旁的流星划过了天际 我许下 的愿望 该向谁 去说明 ——我是如此相信 完整代码见&#xff1a; https://github.com/SnowLegend-star/6.824 还是那句话&#xff0c;尽量只是参考思路而不是照抄 先阅读几遍实验说明的Introd…

Linux-异步IO和存储映射IO

异步IO 在 I/O 多路复用中&#xff0c;进程通过系统调用 select()或 poll()来主动查询文件描述符上是否可以执行 I/O 操作。而在异步 I/O 中&#xff0c;当文件描述符上可以执行 I/O 操作时&#xff0c;进程可以请求内核为自己发送一个信号。之后进程就可以执行任何其它的任务…

R 语言科研绘图第 1 期 --- 折线图-基础

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…

企业中数据防泄漏如何防范?有哪些防泄密措施?

企业数据不仅是业务运营的核心&#xff0c;也是企业竞争力的关键所在。 然而&#xff0c;随着信息技术的快速发展&#xff0c;数据泄露的风险也随之增加。 数据一旦泄露&#xff0c;不仅可能导致企业经济损失&#xff0c;还可能损害企业声誉&#xff0c;甚至引发法律纠纷。 …

汽车控制软件下载移动管家手机控车一键启动app

移动管家手机控制汽车系统是一款实现车辆远程智能控制的应用程序‌。通过下载并安装特定的APP&#xff0c;用户可以轻松实现以下功能&#xff1a;‌远程启动与熄火‌&#xff1a;无论身处何地&#xff0c;只要有网络&#xff0c;即可远程启动或熄火车辆&#xff0c;提前预冷或预…

基于事件驱动构建 AI 原生应用

作者&#xff1a;寒斜 AI 应用在商业化服务的阶段会面临诸多挑战&#xff0c;比如更快的服务交付速度&#xff0c;更实时、精准的结果以及更人性化的体验等&#xff0c;传统架构限制于同步交互&#xff0c;无法满足上述需求&#xff0c;本篇文章给大家分享一下如何基于事件驱动…

如何查看阿里云ddos供给量

要查看阿里云上的 DDoS 攻击量&#xff0c;你可以通过阿里云的 云盾 DDoS 防护 服务来进行监控和查看攻击数据。阿里云提供了详细的流量监控、攻击日志以及攻击趋势分析工具&#xff0c;帮助用户实时了解 DDoS 攻击的情况。以下是九河云总结的查看 DDoS 攻击量的步骤&#xff1…

华为HarmonyOS 让应用快速拥有账号能力 - 获取用户手机号

场景介绍 当应用对获取的手机号时效性要求不高时&#xff0c;可使用Account Kit提供的手机号授权与快速验证能力&#xff0c;向用户发起手机号授权申请&#xff0c;经用户同意授权后&#xff0c;获取到手机号并为用户提供相应服务。以下只针对Account kit提供的手机号授权与快…

React 的学习记录一:与 Vue 的相同点和区别

目录 一、学习目标 二、学习内容1️⃣——React的特点 1.组件化设计 2.单向数据流 3.声明式 UI 4.虚拟 DOM 5.Hooks 6.JSX 7.React Native 三、React与vue的比较总结 四、总结 一、学习目标 时间&#xff1a;两周 内容&#xff1a; React的特点React的入门React的…

使用epoll监测定时器是否到达指定时间,并执行回调函数

总览&#xff1a;Linux提供了定时器&#xff0c;暴露出来了文件描述符&#xff0c;所以我们使用epoll帮助我们监测&#xff0c;时间到达后&#xff0c;epoll_wait返回&#xff0c;于是我们根据fd&#xff0c;找到对应的回调函数&#xff0c;然后执行。从而达到定时执行函数的目…

鸿蒙征文|鸿蒙技术分享:使用到的开发框架和技术概览

目录 每日一句正能量前言正文1. 开发环境搭建关键技术&#xff1a;2. 用户界面开发关键技术&#xff1a;3. 应用逻辑开发关键技术&#xff1a;4. 应用测试关键技术&#xff1a;5. 应用签名和打包关键技术&#xff1a;6. 上架流程关键技术&#xff1a;7. 后续维护和更新关键技术…

【MIT-OS6.S081笔记0.5】xv6 gdb调试环境搭建

补充一下xv6 gdb调试环境的搭建&#xff0c;我这里装的是最新的15.2的gdb的版本。我下载的是下面的第二个xz后缀的文件&#xff1a; 配置最详细的步骤可以参考下面的文章&#xff1a; [MIT 6.S081] Lab 0: 实验配置, 调试及测试 这里记录一下踩过的一些报错&#xff1a; 文…

Python和Java后端开发技术对比

在当今互联网技术飞速发展的时代&#xff0c;后端开发扮演着至关重要的角色。Python和Java作为两大主流的后端开发语言&#xff0c;各自具备独特的优势和应用场景。让我们深入了解这两种技术的特点和选择建议。 Java后端开发一直是企业级应用的首选方案。它以强大的类型系统、…

1.2.3 逻辑代数与运算

逻辑代数与运算 基本的逻辑运算常用逻辑公式 基本的逻辑运算 基本逻辑运算非常简单&#xff0c;只包含与、或、非、异或这4种。 这里主要留意对基本逻辑运算的不同叫法&#xff0c;符号表示。逻辑表达式、真值表概念。 与&#xff1a;A和B都为真时&#xff0c;结果才为真或…

解析生成对抗网络(GAN):原理与应用

目录 一、引言 二、生成对抗网络原理 &#xff08;一&#xff09;基本架构 &#xff08;二&#xff09;训练过程 三、生成对抗网络的应用 &#xff08;一&#xff09;图像生成 无条件图像生成&#xff1a; &#xff08;二&#xff09;数据增强 &#xff08;三&#xff…

零售餐饮收银台源码

收银系统早已成为门店经营的必备软件工具&#xff0c;因为各个连锁品牌有自己的经营模式&#xff0c;自然对收银系统需求各有不同&#xff0c;需要有相应的功能模块来实现其商业模式。 1. 适用行业 收银系统源码适用于零售、餐饮等行业门店&#xff0c;如商超便利店、水果生鲜…

我的第一个创作纪念日 —— 梦开始的地方

前言 时光荏苒&#xff0c;转眼间&#xff0c;我已经在CSDN这片技术沃土上耕耘了365天 今天&#xff0c;我迎来了自己在CSDN的第1个创作纪念日&#xff0c;这个特殊的日子不仅是对我过去努力的肯定&#xff0c;更是对未来持续创作的激励 机缘 回想起初次接触CSDN&#xff0c;那…

mac终端自定义命令打开vscode

1.打开终端配置文件 open -e ~/.bash_profile终端安装了zsh&#xff0c;那么配置文件是.zshrc&#xff08;打开zsh配置&#xff0c;这里举&#x1f330;使用zsh&#xff09; sudo open -e ~/.zshrc 2.在zshrc配置文件中添加新的脚本&#xff08;这里的code就是快捷命令可以进…

计算帧率、每秒过多少次

1、c #include <iostream> #include <opencv2/opencv.hpp> #include <string> #include <thread> #include <atomic>using namespace std;const int NUM_THREADS 1; // 线程数量std::atomic<int> frameCounts[NUM_THREADS]; // 每个线程…

【在Linux世界中追寻伟大的One Piece】读者写者问题与读写锁

目录 1 -> 读者写者问题 1.1 -> 什么是读者写者问题 1.2 -> 读者写者与生产消费者的区别 1.3 -> 如何理解读者写者问题 2 -> 读写锁 2.1 -> 读写锁接口 3 -> 读者优先(Reader-Preference) 4 -> 写者优先(Writer-Preference) 1 -> 读者写者…