残缺棋盘+染色(分治)

2023-10-30 22:50
文章标签 棋盘 分治 染色 残缺

本文主要是介绍残缺棋盘+染色(分治),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

问题描述

残缺棋盘(defective chessboard):是一个有2k×2k个 方格的棋盘,其中恰有一个方格残缺。对于任意k,恰好存在 2 2 k 2^{2k} 22k种不同的残缺棋盘。
在残缺棋盘中,要求用三格板(triominoes)覆盖残缺棋 盘。在覆盖中,任意两个三格板不能重叠,任意一个三 格板不能覆盖残缺方格,但三格板必须覆盖其他所有方格。

基本要求

(1)输入棋盘大小和残缺方格的位置,输出覆盖后的棋盘.
(2)输出棋盘时要着色,共享同一边界的覆盖应着不同的颜色。棋盘是平面图,要求使用最少的颜色覆盖着色

解题思路

我们可以将覆盖 2 k × 2 k 2^k\times 2^k 2k×2k残缺棋盘的问题转化为4个 2 k − 1 × 2 k − 1 × 2^{k-1}\times 2^{k-1}\times 2k1×2k1×覆盖棋盘。

分割后如图所示:
在这里插入图片描述
假设原本残缺的位置位于2号区域,于是我们可以用三格板覆盖1号、3号、4号区域,如图阴影所示,这样原本的一个残缺棋盘问题就转化成了四个小的残缺棋盘,然后再对每个小的棋盘进行分割即可。本题的输入保证了最终能够用这种方法覆盖。

还有一个问题是最少颜色数量,如果自己画一下不难发现,当k=1时,只用一种颜色,其余情况都是三种颜色,那么我们如何在矩阵中赋值来可视化?

我们假设每一步都用一个崭新的颜色,输入k=4,x=3,y=2后,可以得到下面这样的棋盘:

 4  4  5  5  9  9 10 10 25 25 26 26 30 30 31 31 4  3  3  5  9  8  8 10 25 24 24 26 30 29 29 31 6  3  7  7 11 11  8 12 27 24 28 28 32 32 29 33 6  6  0  7  2 11 12 12 27 27 28 23 23 32 33 33 
14 14 15  2  2 19 20 20 35 35 36 36 23 40 41 41 
14 13 15 15 19 19 18 20 35 34 34 36 40 40 39 41 
16 13 13 17 21 18 18 22 37 37 34 38 42 39 39 43 
16 16 17 17 21 21 22 22  1 37 38 38 42 42 43 43 
46 46 47 47 51 51 52  1  1 67 68 68 72 72 73 73 
46 45 45 47 51 50 52 52 67 67 66 68 72 71 71 73 
48 45 49 49 53 50 50 54 69 66 66 70 74 74 71 75 
48 48 49 44 53 53 54 54 69 69 70 70 65 74 75 75 
56 56 57 44 44 61 62 62 77 77 78 65 65 82 83 83 
56 55 57 57 61 61 60 62 77 76 78 78 82 82 81 83 
58 55 55 59 63 60 60 64 79 76 76 80 84 81 81 85 
58 58 59 59 63 63 64 64 79 79 80 80 84 84 85 85 

我们要将这个棋盘中的所有数字都转化成0、1、2、3(其中0是开始的残缺位置)。也就是下面这个样子:

 2  2  3  3  2  2  3  3  2  2  3  3  2  2  3  3 2  1  1  3  2  1  1  3  2  1  1  3  2  1  1  3 3  1  2  2  3  3  1  2  3  1  2  2  3  3  1  2 3  3  0  2  1  3  2  2  3  3  2  1  1  3  2  2 2  2  3  1  1  2  3  3  2  2  3  3  1  2  3  3 2  1  3  3  2  2  1  3  2  1  1  3  2  2  1  3 3  1  1  2  3  1  1  2  3  3  1  2  3  1  1  2 3  3  2  2  3  3  2  2  1  3  2  2  3  3  2  2 2  2  3  3  2  2  3  1  1  2  3  3  2  2  3  3 2  1  1  3  2  1  3  3  2  2  1  3  2  1  1  3 3  1  2  2  3  1  1  2  3  1  1  2  3  3  1  2 3  3  2  1  3  3  2  2  3  3  2  2  1  3  2  2 2  2  3  1  1  2  3  3  2  2  3  1  1  2  3  3 2  1  3  3  2  2  1  3  2  1  3  3  2  2  1  3 3  1  1  2  3  1  1  2  3  1  1  2  3  1  1  2 3  3  2  2  3  3  2  2  3  3  2  2  3  3  2  2 

这个处理起来还是较为简单的,我们知道,每个 4 × 4 4\times 4 4×4的小棋盘区域可以由5个三格板和1个残缺位置构成,我们不妨让当当前棋盘边长大于等于4的时候,中心位置都用1染色,等于4的时候,左上角和右下角用2染色,左下角和右上角都用3染色,这样可以保证共享同一边界的覆盖着不同的颜色。

最骚的操作来了!这个如果你觉得还是有点难看,那么下面这个呢?
在这里插入图片描述
怎么样,不用QT,不用什么乱七八糟的头文件,就可以做到这样的代码,关键在于这样一行代码:

printf("\033[41m  \033[0m");

这个的含义是,输出两个空格,并且两个空格的背景颜色是41m,\033是开始,\033[0m是结束。

更多内容可以见这篇博客,注意,我是Mac系统,同时在clion下做到了这个效果,其他情况我不清楚能不能做到。

windows应该也能做到改变控制台输出字符的背景颜色,这方面可以自己查询一下。

完整代码

//#pragma GCC optimize(2)
//#pragma G++ optimize(2)
//#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <climits>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;class CheckerBoard{
public:CheckerBoard(int _k=0,int _x=0,int _y=0);~CheckerBoard(){for (int i=0; i<CBSize; i++)delete board[i];delete board;}void split(int xl,int yl,int _size,int sx,int sy,int _color);//拆分棋盘void output();int GetTheSize(){return CBSize;}
private:int** board;//棋盘int color;//总的颜色数量int CBSize;//棋盘大小int x,y;//残缺位置
};
CheckerBoard::CheckerBoard(int _k, int _x, int _y)
{CBSize=pow(2,_k);color=0; x=_x; y=_y;board=new int*[CBSize];for (int i=0; i<CBSize; i++){board[i]=new int[CBSize];}for (int i=0; i<CBSize; i++)for (int j=0; j<CBSize; j++)board[i][j]=0;
}
void CheckerBoard::split(int xl, int yl, int _size, int sx, int sy, int _color)
{//左上角坐标,棋盘大小,残缺位置if(_size<2) return;if(_size>=4) _color=1;int dx=xl+_size/2-1;int dy=yl+_size/2-1;color++;if(sx<=dx && sy<=dy){//残缺位置在左上board[dx+1][dy+1]=board[dx+1][dy]=board[dx][dy+1]=_color;split(xl,yl,_size/2,sx,sy,2);//左上split(xl,dy+1,_size/2,dx,dy+1,3);//左下split(dx+1,yl,_size/2,dx+1,dy,3);//右上split(dx+1,dy+1,_size/2,dx+1,dy+1,2);//右下}else if(sx>dx && sy>dy){//残缺位置在右下board[dx][dy]=board[dx+1][dy]=board[dx][dy+1]=_color;split(xl,yl,_size/2,dx,dy,2);split(xl,dy+1,_size/2,dx,dy+1,3);split(dx+1,yl,_size/2,dx+1,dy,3);split(dx+1,dy+1,_size/2,sx,sy,2);}else if(sx<=dx && sy>dy){//残缺位置在右上board[dx][dy]=board[dx+1][dy]=board[dx+1][dy+1]=_color;split(xl,yl,_size/2,dx,dy,2);split(xl,dy+1,_size/2,sx,sy,3);split(dx+1,yl,_size/2,dx+1,dy,3);split(dx+1,dy+1,_size/2,dx+1,dy+1,2);}else if(sx>dx && sy<=dy){//残缺位置在左下board[dx][dy]=board[dx][dy+1]=board[dx+1][dy+1]=_color;split(xl,yl,_size/2,dx,dy,2);split(xl,dy+1,_size/2,dx,dy+1,3);split(dx+1,yl,_size/2,sx,sy,3);split(dx+1,dy+1,_size/2,dx+1,dy+1,2);}
}
void CheckerBoard::output()
{printf("染色后的棋盘如下:\n");for (int i=0; i<CBSize; i++){for (int j=0; j<CBSize; j++){if(board[i][j]==0) printf("\033[41m  \033[0m");else if(board[i][j]==1) printf("\033[45m  \033[0m");else if(board[i][j]==2) printf("\033[43m  \033[0m");else printf("\033[44m  \033[0m");//printf("%2d ",board[i][j]);}printf("\n");}int k=log2(CBSize);printf("一共用了 %d 种颜色,%d 个三格板,相同数字为同一种颜色,0号位置为残缺位置。\n",(k>=2 ? 3:1),color);
}
int main(){int k,x,y;printf("请输入棋盘规模k,最终棋盘边长为pow(2,k):"); scanf("%d",&k);printf("请输入棋盘残缺位置(x,y),直接输入两个用空格隔开的数字即可,注意,棋盘下标从0开始,纵坐标为x,从上到下,横坐标为y,从左到右,请输入:"); scanf("%d %d",&x,&y);int temp=pow(2,k);while(x>=temp || y>=temp || x<0 || y<0){printf("残缺位置不在棋盘内,请重新输入:"); scanf("%d %d",&x,&y);}CheckerBoard chessboard(k,x,y);chessboard.split(0,0,chessboard.GetTheSize(),x,y,1);chessboard.output();return 0;
}

这篇关于残缺棋盘+染色(分治)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/311098

相关文章

Codeforces Round #240 (Div. 2) E分治算法探究1

Codeforces Round #240 (Div. 2) E  http://codeforces.com/contest/415/problem/E 2^n个数,每次操作将其分成2^q份,对于每一份内部的数进行翻转(逆序),每次操作完后输出操作后新序列的逆序对数。 图一:  划分子问题。 图二: 分而治之,=>  合并 。 图三: 回溯:

三维激光扫描点云配准外业棋盘的布设与棋盘坐标测量

文章目录 一、棋盘标定板准备二、棋盘标定板布设三、棋盘标定板坐标测量 一、棋盘标定板准备 三维激光扫描棋盘是用来校准和校正激光扫描仪的重要工具,主要用于提高扫描精度。棋盘标定板通常具有以下特点: 高对比度图案:通常是黑白相间的棋盘格,便于识别。已知尺寸:每个格子的尺寸是已知的,可以用于计算比例和调整。平面标定:帮助校准相机和激光扫描仪之间的位置关系。 使用方法 扫描棋盘:

分治算法与凸包问题

1. 什么是凸包问题? 凸包问题是计算几何中的经典问题。给定二维平面上的点集,凸包是一个最小的凸多边形,它包含了点集中所有的点。你可以把凸包想象成一根松紧带将所有点紧紧包裹住的样子,凸包的边缘仅沿着最外面的点延伸。 2. 分治法简介 分治算法是解决复杂问题的强大策略,它的思想是将问题分解为多个子问题,分别解决这些子问题后再合并得到最终解。凸包问题可以通过分治算法高效地解决,时间复杂度可以达到

【COGS】577 蝗灾 cdq分治

传送门:【COGS】577 蝗灾 题目分析:cdq分治入门题= =。。。。用差分思想将矩阵分成四块来计算。。排序一维,另一维用树状数组解决。 代码如下: #include <cstdio>#include <vector>#include <cstring>#include <algorithm>using namespace std ;#define REP(

【ACdream】1157 Segments cdq分治

传送门:【ACdream】1157 Segments 题目分析:第一题cdq(陈丹琦)分治!cdq_____Orz! 听说cdq分治可以写,就去学了cdq分治了。。 在我们平常使用的分治中,每一个子问题只解决它本身(可以说是封闭的)。 而在cdq分治中,对于划分出来的两个子问题,前一个子问题用来解决后一个子问题而不是它本身。 具体算法流程如下: 1.将整个操作序列分为两个长

【HDU】4871 Shortest-path tree 最短路+点分治

传送门:【HDU】4871 Shortest-path tree 题目分析: 学了点分治后来看这道题,简直就是水题。。。 但是我竟然花了将近一个晚上才写出来。。。就因为一个地方写漏了T U T。。 首先根据题意求一棵树,最短路一下,然后最小字典序就按照编号顺序遍历邻接表给节点标记。 剩下的就是树分治的事了。 在以重心X为根的子树中,按照X的子节点v的子树中最长路径包含节点数升序遍

【HDU】4812 D Tree 点分治

传送门:【HDU】4812 D Tree 题目分析:点分治搞之。乘积等于K的路径。 首先我们定义一个path[ i ]用以记录从根结点x在子树x内的第 i 条路径的值(乘积)。然后每次我们搞完当前重心rt的一棵子树以后,我们用判断K*逆元[ path[ i ] * val[ rt ] %MOD ] % MOD 是否存在来确定乘积为K的路径是否存在,然后再用这个path[ i ]去更

【SPOJ】1825 Free tour II 点分治

传送门:【SPOJ】1825 Free tour II 题目分析:敲了两遍。。。 本题是论文题,具体见漆子超论文《分治算法在树的路径问题中的应用》。 在以root为根的第 i 棵子树上,我们用G[ i ,j ]表示root的第 i 棵子树的路径上严格有 j 个黑点的路径的最长长度。用F[ i ,j ]表示在root为根的第 i 棵子树的路径上不超过 j 个黑点的路径的最长长度。因

【HDU】4670 Cube number on a tree 点分治

传送门:【HDU】4670 Cube number on a tree 题目分析:首先因为至多30个素数,3^30在long long以内,如果一条路径上的数的乘积是个立方数,则这条路径上每个素数因子的个数都应该是3的倍数,于是我们用三进制表示含有素数的状态,当且仅当状态为0(即所有素数的个数都是3的倍数)时这条路径上数的乘积为完全立方数。考虑树分治,每层分治,求出当前重心的一个儿子的一个