• 棋盘覆盖(JavaScript)


    目录

    题目描述

    输入

    输出

    题目解析

    算法代码实现


    题目描述

    在一个2^k * 2^k 个方格组成的棋盘中,恰有一个方格与其他方格不同,称该方格为特殊方格,且称该棋盘为特殊棋盘。

    在棋盘覆盖问题中,要用如下图示的4种不同形态的L型骨牌覆盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖。

    输入

    控制台输入

    第一行为整数k,(不是棋盘大小是2^k * 2^k,1≤k≤10),

    第二行是两个整数,表示特殊方格所在行和列。

    3

    2 2

    输出

    特殊方格用0表示,数据间用制表符隔开,要求遍历顺序按从左到右,从上到下。具体看样例

      3      3      4      4      8      8      9      9
      3      0      2      4      8      7      7      9
      5      2      2      6    10    10      7    11
      5      5      6      6      1    10    11    11
    13    13    14      1      1    18    19    19
    13    12    14    14    18    18    17    19
    15    12    12    16    20    17    17    21
    15    15    16    16    20    20    21    21

    题目解析

    输入

    3

    2 2

    表示创建一个2³ x 2³ 的棋盘,即 8 x 8 棋盘,该棋盘中有一个特殊方格,其位置在棋盘的第2行,第2列。

    按照题目要求,我们可以使用4种不同形态的L型骨牌来覆盖特殊方格(2,2)其外的其他方格,L型骨牌可以用相同的数字来标记,如下图所示:

    此题的解题思路采用分治法

    假设k=1,则此时棋盘为2x2网格

    特殊方格0的位置共有四处可能,如上图所示,

    而无论特殊方格位置在哪,我们都可以使用不同形态的L型骨牌填充剩余格子


    假设k=2,此时棋盘为4x4网格

    特殊方格的放置位置可以有16处,但是特殊方格无论怎么放置,都可以转化如下四种位置(代入旋转,镜像,反转思维看)

    因此,我们将4x4棋盘,分解为了4个2x2子棋盘,则特殊方格可以放置在任意一个2x2子棋盘中,并且放置在子棋盘中的位置只有上图所示的四种情况。

    则此时,特殊方格所在的子棋盘的剩余网格必然可以被L型骨牌覆盖,如下图所示:

    剩下工作就是将剩余的3个2x2的子棋盘用L型骨牌覆盖:

    我们已经知道了一个L型骨牌占据三个方格,因此一个2x2的棋盘只能放入一个L型骨牌,并且还剩1个空闲方格。

    因此3个2x2棋盘可以放3个L型骨牌,还剩余3个空闲方格。而剩余的3个空闲方格数目刚好符合一个L型骨牌所需要占据的方格数。

    因此,我们必须让3个2x2棋盘放完L型骨牌后产生的空闲方格紧挨在一起,这样才能再次凑出一个L型骨牌。

    反过来思考,我们可以先将剩余的3个2x2子棋盘的空闲网格位置先确定了,让3个空闲网格形成L型骨牌

    因此最终4x4棋盘的覆盖策略如下:

    为了保证分治法分解出来的子问题是相同的,我们可以将空闲网格也看成特殊方格。


    假设k=3,则此时棋盘为8 x 8的网格

    按照之前的思路,我们需要将:

    8x8的棋盘,分解为 4个 4x4 的棋盘,则特殊方格0必然在某个4x4棋盘中。

    假设,我们找到了包含特殊方格的4x4棋盘,则我们需要继续将4x4棋盘分解为4个 2x2 棋盘,同样的,特殊方格0必然在某个2x2棋盘中。

    假设,我们找到了包含特殊方格的2x2棋盘,我们称此2x2棋盘为a棋盘,则a棋盘的覆盖策略已经确定,如下图橙色L型骨牌覆盖。

    另外3个2x2棋盘的空闲网格位置也被迫确定了,如下图绿色L型骨牌覆盖。

    而3个2x2棋盘的空闲网格位置确定好了,则它们的L型骨牌覆盖策略也就定了。

    因此一个4x4棋盘的覆盖策略就被确定了,我们称此4x4棋盘为A棋盘。

    通过A棋盘的覆盖,我们发现,一个 2^k * 2^k 的棋盘,放下足够多的L型骨牌后,必然还剩一个空闲网格。比如2x2棋盘只能放下 2x2/3=1...1个,4x4棋盘只能放下4x4/3=5...1,8x8棋盘只能放下8x8/3=21...1。

    因此8x8棋盘中除了A棋盘外的剩余3个4x4子棋盘,无论每个4x4棋盘如何放置L型骨牌,自身必然剩余1个空闲网格。则3个4x4棋盘必然还剩3个空闲网格,则此时我们只能将这三个空闲网格凑在一起,形成L型骨牌。

    同样地,为了保证分治法分解出来的子问题是相同的,我们可以将空闲网格也看成特殊方格。

    因此本题的分解思路应该如下:

    • 将2^k * 2^k棋盘分为4个 2^(k-1) * 2^(k-1)的子棋盘,判断特殊方格位于哪个子棋盘中
    1. 含有特殊方格的子棋盘继续进行分解,直到子棋盘为1x1
    2. 不含有特殊方格的子棋盘,我们人为的为其设定一个特殊方格,即指定它们的空闲方格的为特殊方格。而它们空闲方格的位置,必然是相连的且围绕(含有特殊方格的子棋盘),如下图所示:

    算法代码实现

    1. /* JavaScript Node ACM模式 控制台输入获取 */
    2. const readline = require("readline");
    3. const rl = readline.createInterface({
    4. input: process.stdin,
    5. output: process.stdout,
    6. });
    7. const arr = [];
    8. rl.on("line", (line) => {
    9. arr.push(line);
    10. if (arr.length === 2) { // 让控制台支持多行输入
    11. const k = parseInt(arr[0]);
    12. const [r, c] = arr[1].split(" ").map((item) => parseInt(item));
    13. gridCover(k, r, c).map((row) => {
    14. console.log(row.join("\t"));
    15. });
    16. arr.length = 0; // 让控制台支持不断输入测试
    17. }
    18. });
    19. /* 棋盘覆盖 */
    20. function gridCover(k, row, col) {
    21. // tile是L型骨牌的编号
    22. let tile = 1;
    23. // 棋盘大小 size x size,而size = 2^k
    24. let size = 1 << k;
    25. // 创建一个size x size大小的二维数组作为棋盘
    26. const grid = createGrid(size, size);
    27. // 标记出特殊方格的位置
    28. grid[row - 1][col - 1] = 0;
    29. // tr表示棋盘左上角的行号,tc表示棋盘左上角的列号,dr表示特殊方格的行号,dc表示特殊方格的列好,size表示棋盘的大小
    30. function chessBoard(tr, tc, dr, dc, size) {
    31. if (size === 1) return;
    32. let t = tile++;
    33. let s = size / 2;
    34. // 如果特殊方格0位于左上角子棋盘中,则左上角子棋盘为特殊棋盘
    35. if (dr < tr + s && dc < tc + s) {
    36. // 分解特殊棋盘
    37. chessBoard(tr, tc, dr, dc, s);
    38. } else {
    39. // 否则,左上角子棋盘不是特殊棋盘,我们可以将其空闲方格位置(右下角)定义为特殊方格,让其变为一个特殊棋盘
    40. grid[tr + s - 1][tc + s - 1] = t;
    41. // 分解特殊棋盘
    42. chessBoard(tr, tc, tr + s - 1, tc + s - 1, s);
    43. }
    44. // 如果特殊方格0位于右上角子棋盘中,则右上角子棋盘为特殊棋盘
    45. if (dr < tr + s && dc >= tc + s) {
    46. // 分解特殊棋盘
    47. chessBoard(tr, tc + s, dr, dc, s);
    48. } else {
    49. // 否则,右上角子棋盘不是特殊棋盘,我们可以将其空闲方格位置(左下角)定义为特殊方格,让其变为一个特殊棋盘
    50. grid[tr + s - 1][tc + s] = t;
    51. chessBoard(tr, tc + s, tr + s - 1, tc + s, s);
    52. }
    53. // 如果特殊方格0位于左下角子棋盘中,则左下角子棋盘为特殊棋盘
    54. if (dr >= tr + s && dc < tc + s) {
    55. // 分解特殊棋盘
    56. chessBoard(tr + s, tc, dr, dc, s);
    57. } else {
    58. // 否则,左下角子棋盘不是特殊棋盘,我们可以将其空闲方格位置(右上角)定义为特殊方格,让其变为一个特殊棋盘
    59. grid[tr + s][tc + s - 1] = t;
    60. chessBoard(tr + s, tc, tr + s, tc + s - 1, s);
    61. }
    62. // 如果特殊方格0位于右下角子棋盘中,则右下角子棋盘为特殊棋盘
    63. if (dr >= tr + s && dc >= tc + s) {
    64. // 分解特殊棋盘
    65. chessBoard(tr + s, tc + s, dr, dc, s);
    66. } else {
    67. // 否则,右下角子棋盘不是特殊棋盘,我们可以将其空闲方格位置(左上角)定义为特殊方格,让其变为一个特殊棋盘
    68. grid[tr + s][tc + s] = t;
    69. chessBoard(tr + s, tc + s, tr + s, tc + s, s);
    70. }
    71. }
    72. chessBoard(0, 0, row - 1, col - 1, size);
    73. return grid;
    74. }
    75. /* 创建二维数组 */
    76. function createGrid(row, col) {
    77. const grid = new Array(row);
    78. for (let i = 0; i < row; i++) {
    79. grid[i] = new Array(col);
    80. }
    81. return grid;
    82. }

    在写算法过程中,遇到的比较棘手的问题就是JavaScript创建二维数组。

    我们不能使用 const 2dArr = new Array(8).fill(new Array(8))来创建二维数组,因为数组的fill方法会用入参填充数组元素。如果fill入参是一个对象,则被填充的数组的所有元素都指向该对象。

    所以我们只能老老实实的for循环去创建。

    另外 new Array(8)表示创建一个长度为8的数组,但是此时数组元素都是empty。

    而当数组元素是empty时,数组原型上的一些方法,如forEach、map、filter、some、every、reduce将会跳过这些empty元素,所谓跳过,即这些empty元素不会进入callback回调。这是稀疏数组特性。

  • 相关阅读:
    PyScript:让Python在HTML中运行
    mysql集群使用nginx配置负载均衡
    c++ 各版本特性介绍
    第15章Linux之JavaEE定制篇-搭建JavaEE环境
    搜索引擎分布式系统思考实践
    构建RAG应用-day05: 如何评估 LLM 应用 评估并优化生成部分 评估并优化检索部分
    新版首途影视视频网站源码/22套带后台版全开源+无加密源码(全新二开完整版)
    Linux杀掉僵尸进程方法
    初识链表(7.25)
    强烈 推荐 13 个 Web前端在线代码IDE
  • 原文地址:https://blog.csdn.net/qfc_128220/article/details/126217998