当前位置: 首页 > news >正文

老题新解|棋盘覆盖

《信息学奥赛一本通》第162题:棋盘覆盖

题目描述
在一个 2k×2k2^k \times 2^k2k×2k 个方格组成的棋盘中,恰有一个方格与其他方格不同,称该方格为一特殊方格。现在用 L 型(占 333 小格)骨牌覆盖棋盘上除了特殊方格的所有方格,各骨牌不能重叠。

现给出棋盘的大小和特殊方格所在的位置,请找出这种棋盘。
输入格式
输入两行。
第一行一个正整数 kkk
第二行两个正整数 x,yx,yx,y,表示第 xxx 行第 yyy 列为特殊方格。
输出格式
输出一个 2k2^k2k2k2^k2k 列的棋盘,每两个整数之间由空格隔开。特殊方格用 000 表示。对于其他方格,从 111 开始编号骨牌,输出该方格所属的骨牌编号。
输出任意一种符合要求的答案都算作正确。
输入输出样例 #1
输入 #1
2
2 2
输出 #1
2 2 3 3
2 0 1 3
4 1 1 5
4 4 5 5
说明/提示
本题共有 101010 个子任务,第 iii 个子任务中 k=ik=ik=i。每个子任务记 101010 分,你只有通过每个子任务中所有测试点才能获得该子任务的分数。

大家好,我是莫小特。
这篇文章给大家带来《信息学奥赛一本通》中的第 162 题:棋盘覆盖。

image.png

一、题目描述

洛谷的题号是:B2163 棋盘覆盖

image.png

二、题意分析

这道题是信息学奥赛一本通练习题的第 162 题。

根据输入格式描述,输入两行,第一行为正整数 k,第二行两个正整数 x 和 y,表示第 x 行第 y 列为特殊方格。

使用 int 类型输入数据。

int k;
int x,y;
cin>>k;
cin>>x>>y;

根据题目意思,k 表示输入的幂次,表示棋盘的大小 2k×2k2^k\times2^k2k×2k,所以需要定义一个二维数组来存储这个棋盘,使用 int 类型即可。

2k2^k2k 可以使用位运算实现,定义一个 int 类型的变量来存储。

int n=1<<k;//n=2^k

定义一个棋盘的二维数组,数组名为 board,全部初始值为 0,写在全局变量也可以,默认全部赋值为 0。

int board[1025][1025]={0};//全部赋值为0

接下来需要根据题目意思来完成输出,根据题目描述,要输出 2k2^k2k2k2^k2k 列的棋盘,特殊方格用 0 表示,其他方格,从 1 开始编号,并且输出任意一种符合要求的答案都算正确,所以我们写一个函数来完成这个事情。

而根据题目的描述,要除去特殊的方格,所以我们令特殊的方格为 0,其实前面已经全部初始化为 0 了,保险起见,可以写一下。

board[x][y]=0;

接下来需要完成 L 型骨牌,骨牌的编号从 1 开始,所以定义一个变量存储编号。

int tile_id = 1;//编号从1开始

正式开始写棋盘的函数,因为只有赋值,所以无返回值类型,有5个参数:

(1)x, y: 当前子棋盘左上角,行和列。
(2)s:子棋盘的边长。
(3)r,c:当前子棋盘中的特殊格在全局坐标系中的位置。

void qp(int x,int y,int s,int r,int c)
{if(s==1) return; //大小为1的不放牌int id=tile_id++;//本次放置L型骨牌编号int half=s/2;    //棋盘一半边长int midx=x+half-1; //中心四格的上半行int midy=y+half-1; //中心四格的左半列
}

需要判断特殊格在四个象限中的哪一个。

(1)左上 (top-left)
(2)右上 (top-right)
(3)左下 (bottom-left)
(4)右下 (bottom-right)

定义一个变量存储位置。

int quad;
if (r <= midx && c <= midy) quad = 1;
else if (r <= midx && c > midy) quad = 2;
else if (r > midx && c <= midy) quad = 3;
else quad = 4;

在中心放一个 L 型骨牌,覆盖除包含真实特殊格的象限外的三个中心格
中心四格坐标:

(midx, midy) (midx, midy+1)
(midx+1, midy) (midx+1, midy+1)

所以可写判断。

// 左上中心格
if (quad != 1) board[midx][midy] = id;   
// 右上中心格    
if (quad != 2) board[midx][midy + 1] = id; 
// 左下中心格  
if (quad != 3) board[midx + 1][midy] = id; 
// 右下中心格  
if (quad != 4) board[midx + 1][midy + 1] = id; 

递归处理四个象限:

对于真实含有特殊格的象限,传入真实特殊格坐标;
对于其他象限,把我们刚放的对应中心格视为该象限的新“特殊格”。

// 左上
if (quad == 1) qp(x, y, half, r, c);
else qp(x, y, half, midx, midy);// 右上
if (quad == 2) qp(x, y + half, half, r, c);
else qp(x, y + half, half, midx, midy + 1);// 左下
if (quad == 3) qp(x + half, y, half, r, c);
else qp(x + half, y, half, midx + 1, midy);// 右下
if (quad == 4) qp(x + half, y + half, half, r, c);
else qp(x + half, y + half, half, midx + 1, midy + 1);

在主函数中执行。

qp(1, 1, n, x, y); // 从整个棋盘开始递归覆盖

最终输出即可,使用两重循环,下标从 1 开始到 n。

 for(int i=1;i<=n;i++) {for (int j=1;j<=n;j++){if (j>1) cout<<' ';{cout << board[i][j];}  }cout<<'\n';
}

按照样例输入对数据进行验证。

image.png

符合样例输出,到网站提交测评。

image.png

测试通过!

三、完整代码

该题的完整代码如下:

#include<iostream>
#include<bits/stdc++.h>
using namespace std;
int board[1025][1025]={0};//全部赋值为0
int tile_id = 1;//编号从1开始
void qp(int x,int y,int s,int r,int c)
{if(s==1) return; //大小为1的不放牌int id=tile_id++;//本次放置L型骨牌编号int half=s/2;    //棋盘一半边长int midx=x+half-1; //中心四格的上半行int midy=y+half-1; //中心四格的左半列int quad;if (r <= midx && c <= midy) quad = 1;else if (r <= midx && c > midy) quad = 2;else if (r > midx && c <= midy) quad = 3;else quad = 4;// 左上中心格if (quad != 1) board[midx][midy] = id;   // 右上中心格    if (quad != 2) board[midx][midy + 1] = id; // 左下中心格  if (quad != 3) board[midx + 1][midy] = id; // 右下中心格  if (quad != 4) board[midx + 1][midy + 1] = id; // 左上if (quad == 1) qp(x, y, half, r, c);else qp(x, y, half, midx, midy);// 右上if (quad == 2) qp(x, y + half, half, r, c);else qp(x, y + half, half, midx, midy + 1);// 左下if (quad == 3) qp(x + half, y, half, r, c);else qp(x + half, y, half, midx + 1, midy);// 右下if (quad == 4) qp(x + half, y + half, half, r, c);else qp(x + half, y + half, half, midx + 1, midy + 1);
}
int main()
{int k;int x,y;cin>>k;cin>>x>>y;int n=1<<k;//n=2^kboard[x][y]=0;//// 从整个棋盘开始递归覆盖qp(1, 1, n, x, y); for(int i=1;i<=n;i++) {for (int j=1;j<=n;j++){if (j>1) cout<<' ';{cout << board[i][j];}}cout<<'\n';}return 0;
}

四、总结

本题考察了: 递归分治思想、二维数组坐标变换与边界处理。主要知识点是把大棋盘分成 4 个子棋盘,在中心放 L 形砖并把问题递归到四个子棋盘上。

常见易错点

(1)中心坐标(midx / midy)计算错误

正确写法(若子棋盘左上角为 (x,y),边长 s):

int half = s/2;
int midx = x + half - 1;
int midy = y + half - 1;

错写成 x+half / y+half 会把中心四格错移一个格,覆盖出错。

(2)下标使用混淆(1-based / 0-based)

示例代码采用 1-based 下标(qp(1,1,n, x,y)),数组 board 也按 1..n 存取。

若改为 0-based,则所有坐标 / mid 计算需相应调整。一定在代码中统一采用一种风格并保持一致。

(3)没有把“放入的中心格”作为子象限的特殊格

在放中心 L 形骨牌后,对不含真实特殊格的三个象限,必须把中心被放置的格子当作该子棋盘的特殊格传入递归,否则递归子问题不满足“每个子棋盘恰有一个特殊格”的前提。

(4)tile_id 编号管理错误

tile_id 应在每次放置 L 形骨牌时自增一次(并被四个中心格使用同一个 id)。若在递归中误用局部 id 或忘记自增,会导致不同骨牌重复编号或编号跳跃。

(5)递归终止条件错误

s == 1 时无需放牌(单格子就是特殊格或已被覆盖),必须 return。若写成 s<=1 或在 s==2 时处理不当会导致重复或遗漏覆盖。常见正确处理:if (s==1) return;

(6)越界写入 / 未初始化数组

board 大小需至少 2^k+1 (若用 1-based),建议声明 board[1025][1025] 并初始化为 0。注意赋值 board[midx+1][midy+1] 时不要越界。

(7)输出格式与空格

要求每行 n 个数,用空格隔开且不要行尾多余空格。通常在内层循环:if (j>1) cout << ' '; cout << board[i][j];

---end---

如果你觉得这篇文章对你有帮助,欢迎点赞、收藏、关注我哦!
如果有更好的方法也可以在评论区评论哦,我都会看哒~

我们下集见~

http://www.dtcms.com/a/456853.html

相关文章:

  • 网站可不可以做自己的专利东莞沙田网站建设
  • Redis Hash 全解析:从入门到精通,解锁高性能对象存储的钥匙
  • 14.排序
  • Python自动化实战第一篇: 自动化备份100+台服务器Web 配置
  • 第五十二章 ESP32S3 UDP 实验
  • [鹤城杯 2021]Misc2
  • 山东省旅游网站建设网络设计是干什么的工作
  • 基于 ZYNQ ARM+FPGA+AI YOLOV4 的电网悬垂绝缘子缺陷检测系统的研究
  • 开源 C++ QT QML 开发(十二)通讯--TCP客户端
  • 【密码学实战】openHiTLS pkeyutl命令行:公钥实用工具(加解密、密钥交换)
  • 做标书有什么好的网站吗网站改版不收录
  • JDK17和JDK8的 G1
  • win10安装conda环境
  • TDengine 浮点数新编码 BSS 用户手册
  • mybatis call存储过程,out的参数怎么返回
  • 今日八股——JVM篇
  • 【论文阅读】REACT: SYNERGIZING REASONING AND ACTING IN LANGUAGE MODELS
  • 沈阳做网站比较好的公司做网站需要会的软件
  • ubuntu22.04安装gvm管理go
  • 基于单片机的智能点滴输液速度与液位控制系统设计
  • 嵌入式开发学习日志38——stm32之看门狗
  • golang面经——内存相关模块
  • 成都政务网站建设怎样做视频网站
  • 架构设计常画哪些图
  • 自然语言处理分享系列-词向量空间中的高效表示估计(一)
  • RNN的注意力机制:原理与实现(代码示例)
  • Flutter bottomNavigationBar 底部导航栏
  • 做男装去哪个网站好的网站开发工具有哪些
  • 【Spring 3】深入剖析 Spring 的 Prototype Scope:何时以及如何使用非单例 Bean
  • asp.net+mvc+网站开发wordpress 手机端页面