殘缺棋盤+染色(分治)

問題描述

殘缺棋盤(defective chessboard):是一個有2k×2k個 方格的棋盤,其中恰有一個方格殘缺。對於任意k,恰好存在22k2^{2k}種不同的殘缺棋盤。
在殘缺棋盤中,要求用三格板(triominoes)覆蓋殘缺棋 盤。在覆蓋中,任意兩個三格板不能重疊,任意一個三 格板不能覆蓋殘缺方格,但三格板必須覆蓋其他所有方格。

基本要求

(1)輸入棋盤大小和殘缺方格的位置,輸出覆蓋後的棋盤.
(2)輸出棋盤時要着色,共享同一邊界的覆蓋應着不同的顏色。棋盤是平面圖,要求使用最少的顏色覆蓋着色

解題思路

我們可以將覆蓋2k×2k2^k\times 2^k殘缺棋盤的問題轉化爲4個2k1×2k1×2^{k-1}\times 2^{k-1}\times覆蓋棋盤。

分割後如圖所示:
在這裏插入圖片描述
假設原本殘缺的位置位於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×44\times 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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章