Leetcode打卡:棋盘上有效移动组合的数目

news2025/1/23 13:17:56

执行结果:通过

题目:2056 棋盘上有效移动组合的数目

有一个 8 x 8 的棋盘,它包含 n 个棋子(棋子包括车,后和象三种)。给你一个长度为 n 的字符串数组 pieces ,其中 pieces[i] 表示第 i 个棋子的类型(车,后或象)。除此以外,还给你一个长度为 n 的二维整数数组 positions ,其中 positions[i] = [ri, ci] 表示第 i 个棋子现在在棋盘上的位置为 (ri, ci) ,棋盘下标从 1 开始。

棋盘上每个棋子都可以移动 至多一次 。每个棋子的移动中,首先选择移动的 方向 ,然后选择 移动的步数 ,同时你要确保移动过程中棋子不能移到棋盘以外的地方。棋子需按照以下规则移动:

  • 车可以 水平或者竖直 从 (r, c) 沿着方向 (r+1, c)(r-1, c)(r, c+1) 或者 (r, c-1) 移动。
  • 后可以 水平竖直或者斜对角 从 (r, c) 沿着方向 (r+1, c)(r-1, c)(r, c+1)(r, c-1)(r+1, c+1)(r+1, c-1)(r-1, c+1)(r-1, c-1) 移动。
  • 象可以 斜对角 从 (r, c) 沿着方向 (r+1, c+1)(r+1, c-1)(r-1, c+1)(r-1, c-1) 移动。

移动组合 包含所有棋子的 移动 。每一秒,每个棋子都沿着它们选择的方向往前移动 一步 ,直到它们到达目标位置。所有棋子从时刻 0 开始移动。如果在某个时刻,两个或者更多棋子占据了同一个格子,那么这个移动组合 不有效 。

请你返回 有效 移动组合的数目。

注意:

  • 初始时,不会有两个棋子 在 同一个位置 。
  • 有可能在一个移动组合中,有棋子不移动。
  • 如果两个棋子 直接相邻 且两个棋子下一秒要互相占据对方的位置,可以将它们在同一秒内 交换位置 。

示例 1:

输入:pieces = ["rook"], positions = [[1,1]]
输出:15
解释:上图展示了棋子所有可能的移动。

示例 2:

输入:pieces = ["queen"], positions = [[1,1]]
输出:22
解释:上图展示了棋子所有可能的移动。

示例 3:

输入:pieces = ["bishop"], positions = [[4,3]]
输出:12
解释:上图展示了棋子所有可能的移动。

示例 4:

输入:pieces = ["rook","rook"], positions = [[1,1],[8,8]]
输出:223
解释:每个车有 15 种移动,所以总共有 15 * 15 = 225 种移动组合。
但是,有两个是不有效的移动组合:
- 将两个车都移动到 (8, 1) ,会导致它们在同一个格子相遇。
- 将两个车都移动到 (1, 8) ,会导致它们在同一个格子相遇。
所以,总共有 225 - 2 = 223 种有效移动组合。
注意,有两种有效的移动组合,分别是一个车在 (1, 8) ,另一个车在 (8, 1) 。
即使棋盘状态是相同的,这两个移动组合被视为不同的,因为每个棋子移动操作是不相同的。

示例 5:

输入:pieces = ["queen","bishop"], positions = [[5,7],[3,4]]
输出:281
解释:总共有 12 * 24 = 288 种移动组合。
但是,有一些不有效的移动组合:
- 如果后停在 (6, 7) ,它会阻挡象到达 (6, 7) 或者 (7, 8) 。
- 如果后停在 (5, 6) ,它会阻挡象到达 (5, 6) ,(6, 7) 或者 (7, 8) 。
- 如果象停在 (5, 2) ,它会阻挡后到达 (5, 2) 或者 (5, 1) 。
在 288 个移动组合当中,281 个是有效的。

提示:

  • n == pieces.length
  • n == positions.length
  • 1 <= n <= 4
  • pieces 只包含字符串 "rook" ,"queen" 和 "bishop" 。
  • 棋盘上最多只有一个后。
  • 1 <= ri, ci <= 8
  • 每一个 positions[i] 互不相同。

代码以及解题思路

代码:

int rookDirections[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int bishopDirections[4][2] = {{1, 1}, {1, -1}, {-1, 1}, {-1, -1}};
int queenDirections[8][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}, {1, 1}, {1, -1}, {-1, 1}, {-1, -1}};

#define MAX_STACK_SIZE 1024

typedef struct {
    int startX, startY, endX, endY, dx, dy, curX, curY;
} Movement;

void initMovement(Movement *obj, int startX, int startY, int endX, int endY, int dx, int dy) {
    obj->startX = startX;
    obj->startY = startY;
    obj->endX = endX;
    obj->endY = endY;
    obj->dx = dx;
    obj->dy = dy;
    obj->curX = startX;
    obj->curY = startX;
}

// Reset 重置棋子的当前位置
void reset(Movement *obj) {
    obj->curX = obj->startX;
    obj->curY = obj->startY;
}

// Stopped 判断棋子是否停止
bool stopped(Movement *obj) {
    return obj->curX == obj->endX && obj->curY == obj->endY;
}

// Advance 让棋子按照步长移动
void advance(Movement *obj) {
    if (!stopped(obj)) {
        obj->curX += obj->dx;
        obj->curY += obj->dy;
    }
}

// Cross 判断两个棋子是否相遇
bool cross(Movement *m1, Movement *m2) {
    // 每次判断是否相遇时需要重置 cur
    reset(m1);
    reset(m2);
    while (!stopped(m1) || !stopped(m2)) {
        advance(m1);
        advance(m2);
        if (m1->curX == m2->curX && m1->curY == m2->curY) {
            return true;
        }
    }
    return false;
}

// Check 判断第 u 个棋子是否与之前的棋子发生相交
bool check(int u, Movement *stack) {
    for (int v = 0; v < u; v++) {
        Movement *m1 = &stack[u], *m2 = &stack[v];
        if (cross(m1, m2)) {
            return false;
        }
    }
    return true;
}

void dfs(int u, char** pieces, int piecesSize, int** positions, Movement *stack, int *res) {
    if (u == piecesSize) {
        (*res)++;
        return;
    }
    // 处理第 u 个棋子原地不动的情况
    initMovement(&stack[u], positions[u][0], positions[u][1], positions[u][0], positions[u][1], 0, 0);
    if (check(u, stack)) {
        dfs(u + 1, pieces, piecesSize, positions, stack, res);
    }

    // 枚举第 u 个棋子在所有方向、所有步数的情况
    int (*directions)[2];
    int directionsSize = 0;
    if (!strcmp(pieces[u], "rook")) {
        directions = rookDirections;
        directionsSize = 4;
    } else if (!strcmp(pieces[u], "queen")) {
        directions = queenDirections;
        directionsSize = 8;
    } else {
        directions = bishopDirections;
        directionsSize = 4;
    }
    for (int i = 0; i < directionsSize; i++) {
        for (int j = 1; j < 8; j++) {
            int x = positions[u][0] + directions[i][0] * j;
            int y = positions[u][1] + directions[i][1] * j;
            if (x < 1 || x > 8 || y < 1 || y > 8) {
                break;
            }
            initMovement(&stack[u], positions[u][0], positions[u][1], x, y, directions[i][0], directions[i][1]);
            if (check(u, stack)) {
                dfs(u + 1, pieces, piecesSize, positions, stack, res);
            }
        }
    }
}

int countCombinations(char** pieces, int piecesSize, int** positions, int positionsSize, int* positionsColSize) {
    int res = 0;
    Movement stack[MAX_STACK_SIZE];
    dfs(0, pieces, piecesSize, positions, stack, &res);
    return res;

}

解题思路:

这个问题是关于在国际象棋棋盘上,给定一些棋子的类型和初始位置,计算它们移动到目标位置(这里假设目标位置是它们能移动到的任意合法位置,不一定是初始位置)的所有可能组合的数量,同时要求任意两个棋子在移动过程中不能相遇。棋子的类型包括车(rook)、象(bishop)和后(queen),每种棋子有不同的移动规则:

  • 车(rook)可以沿直线水平或垂直移动任意步数。
  • 象(bishop)可以沿对角线移动任意步数。
  • 后(queen)可以沿直线或对角线移动任意步数。

解题思路如下:

  1. 定义数据结构
    • 使用 Movement 结构体来表示棋子的移动状态,包括起始位置、结束位置、当前位置、以及移动的方向(dx, dy)。
    • 使用 rookDirectionsbishopDirections 和 queenDirections 数组来定义每种棋子的移动方向。
  2. 初始化
    • initMovement 函数用于初始化棋子的移动状态。
    • reset 函数用于重置棋子的当前位置到起始位置。
    • stopped 函数用于判断棋子是否已到达结束位置。
    • advance 函数用于按步长移动棋子。
  3. 判断相遇
    • cross 函数用于判断两个棋子在移动过程中是否会相遇。它首先重置两个棋子的当前位置,然后按照它们的移动规则逐步移动,如果在任何一步两个棋子的位置相同,则表示它们相遇。
  4. 检查合法性
    • check 函数用于检查在添加一个新的棋子移动方案后,该方案是否与之前的棋子移动方案发生冲突(即是否有棋子相遇)。
  5. 深度优先搜索(DFS)
    • dfs 函数是核心递归函数,用于枚举所有可能的棋子移动组合。
    • 它首先处理每个棋子原地不动的情况。
    • 然后,对于每种棋子和每个方向,它枚举棋子在该方向上移动1到7步(假设棋盘是8x8的)的所有可能情况。
    • 对于每种情况,如果它不与之前的棋子移动方案冲突,则递归地继续处理下一个棋子。
    • 当所有棋子都被处理完毕时,找到一个合法的组合,结果计数加一。
  6. 主函数
    • countCombinations 函数是程序的入口,它初始化结果变量和 Movement 栈,然后调用 dfs 函数开始搜索。
    • 最后,返回计算得到的合法组合数量。

通过这种方式,程序能够枚举所有可能的棋子移动组合,同时确保任意两个棋子在移动过程中不会相遇,从而计算出所有合法的组合数量。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Day5:生信新手笔记 — R语言基本语法

一、数据类型 &#xff08;重点只有两个&#xff0c;剩下的不看&#xff09; 1.1 向量&#xff08;vector&#xff09; 矩阵&#xff08;Matrix&#xff09; 数组&#xff08;Array&#xff09; 1.2 数据框&#xff08;Data frame&#xff09; x<- c(1,2,3) #常用的向…

【Win11的Bug】无法在文件夹中创建txt文件

问题 右键只能新建文件夹 , 无法新建txt文本文档 解决办法 将注册表中的一个参数从1改为0即可. 具体内容: WinR输入regeditHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System 将1改为0(下面这张图我已改过) 4.然后重新启动电脑即可 小技…

word如何快速创建目录?

文章目录 1&#xff0c;先自己写出目录的各级标题。2、选中目标标题&#xff0c;然后给它们编号3、给标题按照个人需求开始分级4、插入域构建目录。4.1、利用快捷键插入域构建目录4.2、手动插入域构建目录 听懂掌声&#xff01;学会了吗&#xff1f; 前提声明&#xff1a;我在此…

Java程序调kubernetes(k8s1.30.7)core API简单示例,并解决403权限验证问题,即何进行进行权限授权以及验证

简单记录问题 一、问题描述 希望通过Java程序使用Kubernetes提供的工具包实现对Kubernetes集群core API的调用&#xff0c;但是在高版本上遇见权限验证问题4xx。 <dependency><groupId>io.kubernetes</groupId><artifactId>client-java</artifact…

合合信息扫描全能王线下体验活动:科技与人文的完美交融

文章目录 前言签到欢迎仪式产品体验智能高清滤镜去除透字效果照片高清修复 破冰行动会议感受 前言 作为合合信息旗下扫描全能王的忠实粉丝&#xff0c;上周&#xff0c;我很荣幸参与了扫描全能王“扫出你的能量buff”快闪活动及技术交流会。这次活动的不仅让我对这款强大的文档…

【工具变量】上市公司企业所在地城市等级直辖市、副省级城市、省会城市 计划单列市(2005-2022年)

一、包含指标&#xff1a; 股票代码 股票代码 股票简称 年份 所属城市 直辖市&#xff1a;企业所在地是否属于直辖市。1是&#xff0c;0否。 副省级城市&#xff1a;企业所在地是否属于副省级城市。1是&#xff0c;0否。 省会城市&a…

Svn如何切换删除账号

记录Svn清除切换账号 1.首先打开小乌龟的设置如下图 打开设置后单击已保存数据&#xff0c;然后选择清除 接上图选择清除后&#xff0c;就可以打勾选择清除已保存的账号&#xff0c;我们再次检出的就可以切换账号了 &#x1f449;总结 本次记录Svn清除切换账号 如能帮助到你…

ASP.NET Core SignalR 入门

一、简介 &#x1f4e2; SignalR的主要功能是提供服务器和客户端之间的实时通信。当连接的客户端变得可用时&#xff0c;服务器可以立即向其推送内容&#xff0c;而不是等待客户端发起请求。这种功能特别适合需要实时更新数据的应用场景&#xff0c;如聊天应用、实时数据分析、…

分布式光伏电站如何实现监控及集中运维管理?

安科瑞戴婷 Acrel-Fanny 前言 今年以来&#xff0c;在政策利好推动下光伏、风力发电、电化学储能及抽水蓄能等新能源行业发展迅速&#xff0c;装机容量均大幅度增长&#xff0c;新能源发电已经成为新型电力系统重要的组成部分&#xff0c;同时这也导致新型电力系统比传统的电…

2022-12-4----Android11(H713m)---- WiFi驱动添加写入mac号补丁

一、问题 用全志的写号工具&#xff0c;写入wifi_mac&#xff0c;设置下边不生效 二、分析 因为我们的WiFi不是用全志平台的&#xff0c;也不是用全志集成好的&#xff0c;而是用希微这家第三方的WiFi/BT&#xff0c;所以该驱动还没完善。 三、修改前的准备 用写号工具写号…

Linux笔记---进程:进程替换

1. 进程替换的概念 进程替换是指在一个正在运行的进程中&#xff0c;用一个新的程序替换当前进程的代码和数据&#xff0c;使得进程开始执行新的程序&#xff0c;而不是原来的程序。 这种技术通常用于在不创建新进程的情况下&#xff0c;改变进程的行为。 我们之前谈到过for…

Linux 权限管理:用户分类、权限解读与常见问题剖析

&#x1f31f; 快来参与讨论&#x1f4ac;&#xff0c;点赞&#x1f44d;、收藏⭐、分享&#x1f4e4;&#xff0c;共创活力社区。&#x1f31f; &#x1f6a9;用通俗易懂且不失专业性的文字&#xff0c;讲解计算机领域那些看似枯燥的知识点&#x1f6a9; 目录 &#x1f4af;L…

【开源】A060-基于Spring Boot的游戏交易系统的设计与实现

&#x1f64a;作者简介&#xff1a;在校研究生&#xff0c;拥有计算机专业的研究生开发团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看项目链接获取⬇️&#xff0c;记得注明来意哦~&#x1f339; 赠送计算机毕业设计600个选题ex…

rabbitmq 安装延时队列插件rabbitmq_delayer_message_exchange(linux centOS 7)

1.插件版本 插件地址&#xff1a;Community Plugins | RabbitMQ rabbitmq插件需要对应的版本&#xff0c;根据插件地址找到插件 rabbitmq_delayer_message_exchange 点击Releases 因为我rabbitmq客户端显示的版本是&#xff1a; 所以我选择插件版本是&#xff1a; 下载 .ez文…

elementui table滚动分页加载

文章目录 概要 简化的实现示例&#xff1a; 小结 概要 在使用 Element UI 的 Table 组件时&#xff0c;如果需要实现滚动分页加载的功能&#xff0c;可以通过监听 Table 的滚动事件来动态加载更多数据。 简化的实现示例&#xff1a; <template><el-table ref"…

书生浦语第四期--基础岛-第五关

文章目录 学习使用Xtuner微调大模型开始微调检查hf文件夹检查meraged文件夹启用WebUI 对话查看效果 学习使用Xtuner微调大模型 开始微调 检查hf文件夹 检查meraged文件夹 启用WebUI 对话 查看效果

基于Transformer的编码器-解码器图像描述模型在AMD GPU上的应用

Transformer based Encoder-Decoder models for image-captioning on AMD GPUs — ROCm Blogs 图像描述&#xff0c;即基于生成式人工智能&#xff08;GenAI&#xff09;自动生成简洁的图像文本描述&#xff0c;在现实世界中有着非常重要的应用。例如&#xff0c;图像描述可以为…

解决Jupyter Notebook无法转化为Pdf的问题(基于Typora非常实用)

笔者在完成各项作业和做笔记时&#xff0c;经常用到jupyter notebook&#xff1b;其因为可以同时运行python并提供格式化的数字公式的输入方式&#xff0c;得到了广大用户的喜爱。 当我们想要将.ipynb文件导出为pdf时&#xff0c;有两种常用方法。 1.Ctrlp 2.通过File ->…

ROS-plotjuggler的使用

简介 一个基于Qt的应用程序&#xff0c;允许用户加载、搜索和打印数据。通常对rosbag画图的辅助工具使用。 使用方法 启动 rosrun plotjuggler plotjuggler打开界面左上角&#xff0c;加载并选择相应数据 如果要用两个数据绘图&#xff0c;则ctrl选中后右键拖入

【笔记】离散数学 1-3 章

1. 数理逻辑 1.1 命题逻辑的基本概念 1.1.1 命题的概念 命题&#xff08;Proposition&#xff09;&#xff1a;是一个陈述句&#xff0c;它要么是真的&#xff08;true&#xff09;&#xff0c;要么是假的&#xff08;false&#xff09;&#xff0c;但不能同时为真和假。例如…