问题描述
残缺棋盘(defective chessboard):是一个有2k×2k个 方格的棋盘,其中恰有一个方格残缺。对于任意k,恰好存在种不同的残缺棋盘。
在残缺棋盘中,要求用三格板(triominoes)覆盖残缺棋 盘。在覆盖中,任意两个三格板不能重叠,任意一个三 格板不能覆盖残缺方格,但三格板必须覆盖其他所有方格。
基本要求
(1)输入棋盘大小和残缺方格的位置,输出覆盖后的棋盘.
(2)输出棋盘时要着色,共享同一边界的覆盖应着不同的颜色。棋盘是平面图,要求使用最少的颜色覆盖着色
解题思路
我们可以将覆盖残缺棋盘的问题转化为4个覆盖棋盘。
分割后如图所示:
假设原本残缺的位置位于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
这个处理起来还是较为简单的,我们知道,每个的小棋盘区域可以由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;
}