首页 > 编程语言 >棋盘覆盖——分治算法的典例

棋盘覆盖——分治算法的典例

时间:2023-10-21 15:59:34浏览次数:39  
标签:典例 方格 分治 tr chess 棋盘 tc size

问题描述

在一个\({2^k} \times {2^k}(K \geqslant 0)\) 个方格组成的棋盘中,恰有一个方格与其他方格不同,称该方格为特殊方格。 棋盘覆盖问题要求用图所示的4种不同形状的\(L\)型骨牌覆盖给定棋盘上除特殊方格以外的所有方格,且任何2个\(L\)型骨牌不得重叠覆盖。

问题分析

算法设计

算法主要实现两个状态:

  1. 有特殊方格的棋盘
    如果棋盘大小大于1*1,那么对棋盘进行划分,使 的棋盘划分为四个 的子棋盘,其中有一个是有特殊方格的子棋盘,有三个是无特殊方格的子棋盘。

  2. 无特殊方格的棋盘
    情况二还分以下四个情况:
    2.1. 是未划分前棋盘的左上子棋盘:将最右下格填充为特殊方格
    2.2. 是未划分前棋盘的右上子棋盘:将最左下格填充为特殊方格
    2.3. 是未划分前棋盘的左下子棋盘:将最右上格填充为特殊方格
    2.4. 是未划分前棋盘的右下子棋盘:将最左上格填充为特殊方格
    之后按照情况一继续划分。
    可以采用分治的思想进行算法设计,解决这个问题需要如下变量及函数。

  3. 棋盘:表示为一个二维数组chess[][],大小由题目要求限制。

  4. 骨牌:骨牌需要在函数里填充,每次完整调用一个函数,视作填充一个骨牌,用cnt变量来作为骨牌标识。

  5. 填充函数:用get_chess表示填充函数,包括形参:
    3.1. tr——当前状态棋盘的左上角行号
    3.2. tc——当前状态棋盘的左上角列号
    3.3. dr——特殊方格的行号
    3.4. dc——特殊方格的列号
    3.5. k——棋盘的规模
    函数的作用包括:划分、填充与合并。

算法复杂度分析

时间复杂度

从分治策略的角度可以看出,该算法满足以下递归方程

\[T(k)= \begin{cases} O(1) , & k = 0\\ 4T(k-1)+O(1),& k>0 \end{cases} \]

解该递归方程可以得出,该算法的时间复杂度为
\(T(K)=O(4^k)\)

算法实现

棋盘覆盖代码
#include <iostream>
#include <algorithm>
#include <iomanip>

#define MAXN 16385

using namespace std;

int chess[MAXN][MAXN];

int cnt = -1;//棋子标记

void get_chess(const int &tr/*左上*/, const int &tc/*左上*/, const int &k/*规模*/,
               const int &dr, const int &dc) {
    if (k == 1) return;//最小子问题
    int size = k / 2;//规模划分
    int _tr = tr;
    int _tc = tc;
    int L = ++cnt;
    //左上
    if (dc >= _tc && dr >= _tr && dr < _tr + size && dc < _tc + size)//特殊方格
    {
        get_chess(_tr, _tc, size, dr, dc);
    } else {
        chess[_tr + size - 1][_tc + size - 1] = L + '0';//标记右下角
        get_chess(_tr, _tc, size, _tr + size - 1, _tc + size - 1);
    }
    //右上
    _tr = tr;
    _tc = tc + size;
    if (dc >= _tc && dr >= _tr && dr < _tr + size && dc < _tc + size)//特殊方格
    {
        get_chess(_tr, _tc, size, dr, dc);
    } else {
        chess[_tr + size - 1][_tc] = L + '0';//标记左下角
        get_chess(_tr, _tc, size, _tr + size - 1, _tc);
    }
    _tr = tr + size;
    _tc = tc;

    if (dc >= _tc && dr >= _tr && dr < _tr + size && dc < _tc + size)//特殊方格
    {
        get_chess(_tr, _tc, size, dr, dc);
    } else {
        chess[_tr][_tc + size - 1] = L + '0';//标记右上角
        get_chess(_tr, _tc, size, _tr, _tc + size - 1);
    }
    //右下
    _tr = tr + size;
    _tc = tc + size;
    if (dc >= _tc && dr >= _tr && dr < _tr + size && dc < _tc + size)//特殊方格
    {
        get_chess(_tr, _tc, size, dr, dc);
    } else {
        chess[_tr][_tc] = L + '0';//标记左上角
        get_chess(_tr, _tc, size, _tr, _tc);
    }
}


int main() {
    int k, dr, dc;
    cin >> k >> dr >> dc;
    chess[dr + 1][dc + 1] = 'd';
    get_chess(1, 1, 1 << k, dr + 1, dc + 1);
    for (int i = 1; i <= 1 << k; i++) {
        for (int j = 1; j <= 1 << k; j++) {
            cout << setfill(' ') << setw(4) << chess[i][j];
        }
        cout << '\n';
    }
    return 0;
}


标签:典例,方格,分治,tr,chess,棋盘,tc,size
From: https://www.cnblogs.com/wzu-hqj/p/17779070.html

相关文章

  • 根号分治
    前言因为觉得这个思想很有意思,最近也见到了许多使用根号分治的题目,自己也出了一些用根号分治的题目,所以想总结一下。(下文各种根号分治的名字是我掰出来的,应该有别的称呼)对文章的细节有疑问或是发现错误的欢迎提出。介绍根号分治是一种在对数据规模分类讨论的基础上利用不同算......
  • 【根号分治】P9212 「蓬莱人形」 题解
    P9212看到除法相关容易想到根号分治。先对\(x,y\)进行讨论,不妨令\(0\lex,y<m\)。\(x<y\)时,当满足\(a_i+y<m\)或\(a_i+x\gem\)时,即当\(a_i<m-y\)或\(a_i\gem-x\)满足\((a_i+x)\bmodm<(a_i+y)\bmodm\),即\(a_i\bmodm\in[0,m-y-1]\bigcup[m-x,m......
  • CDQ分治和三维偏序
    专题:CDQ分治本页面将完整介绍CDQ分治。简介CDQ分治是一种思想而不是具体的算法,与动态规划类似。目前这个思想的拓展十分广泛,依原理与写法的不同,大致分为三类:解决和点对有关的问题。1D动态规划的优化与转移。通过CDQ分治,将一些动态问题转化为静态问题。CDQ分治的......
  • cdq 分治
    cdq分治的思路仍是处理跨过当前区间中点的贡献,但递归顺序是左子区间、当前区间、右子区间。这仍然满足处理当前区间时两个子区间的相对顺序不变(但左子区间不一定是有序的)从嵌套数据结构的观点看,cdq分治就是树套树外层树的中序遍历,优点是空间少一个\(\log\)且常数更小LG4093......
  • 点分治
    点分治1.给定一个带边权的树,共有\(m\)个询问,询问距离为\(k\)的点对是否存在做法1:暴力dfs做法2:\(lca\)(时间复杂度\(O(n^2\logn)\))做法3:点分治(时间复杂度\(O(n\logn)\))思路:1.取一个节点\(u\)2.统计经过\(u\)的链经过\(u\)的链两端点必定在不同子树中记录子节......
  • 递归分治法在快速排序中的应用 java以界面的方式实现
    递归分治法在快速排序中的应用 分治法的基本思想§分治法的基本思想是将一个规模为n的问题分解为k个规模较小的子问题,这些子问题互相独立且与原问题相同。 k个子问题分别求解。如果子问题的规模仍然不够小,则再划分为k个子问题,如此递归的进行下去,直到问题规模足够小,很容易求......
  • 72ed 2023/8/25 点分治学习笔记
    起因&介绍8月22号的T3是道黑,但思路却不算太难,就去打了这是第一次接触点分治,其实之前也有过一道点分治题,叫阴阳,但当时没去改,就一拖拖了半年才学点分治类似于树形DP,但在一些地方上处理有不同就比如在跑过根结点(1),进入处理它的子树时,会将其他的一部分视作没有(emmm大概这个意思,子树......
  • P3956 [NOIP2017 普及组] 棋盘
    传送门P3956[NOIP2017普及组]棋盘不清楚曾师为什么把这个神奇的题目放在搜索\(search\)专栏,反正我用\(dijkstra\)水过去了,虽然\(dijkstra\)严格来说也是一种能够解决一般性最短路问题的算法。然后考虑这道题的建图。这道题来看首先是去除魔法的部分,一般地,任意一个点只......
  • 基本技巧——根号分治 学习笔记
    基本技巧——根号分治学习笔记根号分治与其说是一个算法,更不如说是一种思想(trick)。定义根号分治,是一种对数据进行点分治的分治方式,它的作用是优化暴力算法;类似于分块,但应用范围比分块更广。具体来说,对于所进行的操作,按照某个点\(B\)划分,分为大于\(B\)及小于\(B\)两个部......
  • 线段树分治&可撤销并查集
    可撤销并查集按时间顺序用一个栈维护合并信息,撤销时从栈顶弹出合并信息,恢复原状态。并查集查找祖先时不能路径压缩,只能按秩合并。例题:[ABC302Ex]BallCollector容易想到将\(A_i\)和\(B_i\)之间连边。遍历整棵树,用可撤销并查集维护图。为了进一步求得答案,需要注意到该......