残缺棋盘的覆盖问题

Posted 寄蜉蝣于天地,渺沧海之一粟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了残缺棋盘的覆盖问题相关的知识,希望对你有一定的参考价值。

棋盘覆盖问题   

问题描述:

      在一个2^k×2^k个方格组成的棋盘中,若有一个方格与其他方格不同,则称该方格为一特殊方格,且称该棋盘为一个特殊棋盘.显然特殊方格在棋盘上出现的位置有4^k种情形.因而对任何k≥0,有4^k种不同的特殊棋盘.
     下图–图(1)中的特殊棋盘是当k=3时16个特殊棋盘中的一个:

图(1)

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

图(2)

题目输入k,输出棋盘覆盖的顺序。覆盖策略可以自己选择。

分析:

这个题其实可以有不同的覆盖顺序。比如:(下图中的数字表示依次覆盖的顺序)

    

策略a                                            策略b                                             策略c

策略a:

不停地分割寻找残缺块所在区域,然后在残缺块周围填充第一块(这个时候size=2);然后回溯一层,填充伪残缺块(这个时候size=4);然后对当前size=4的另外三个子棋盘依次填充(含残缺块的子棋盘当然不再填充了。)。
填充完size==4的棋盘后,回溯到size=8的棋盘(假如存在size=8的棋盘的话呵呵),重复刚才的过程:先填充size=8这个棋盘的伪残缺块,然后填充另外的三个子棋盘。……

关键代码如下:

完整代码:

 1 #include<stdio.h>
 2 int count=0,bord[100][100];
 3 //覆盖以(tr,tc)为左上角坐标,宽为size的棋盘。残缺块或伪残缺块坐标为(dr,dc) 
 4 int cover(int tr,int tc,int size,int dr,int dc);
 5 int main()
 6 {
 7     int x,y,k,size=1;
 8     int i,j;
 9     scanf("%d%d%d",&k,&x,&y);
10     for(i=1;i<=k;i++) size=size*2;//计算2^k 
11     cover(0,0,size,x,y);//对原始棋盘进行覆盖 
12     for(i=0;i<size;i++)//输出棋盘的覆盖结果 
13     {
14         for(j=0;j<size;j++)
15         {
16             printf("%-2d ",bord[i][j]);
17         }
18         printf("\\n");
19     }
20     return 0;
21 }
22 
23 int cover(int tr,int tc,int size,int dr,int dc)
24 {
25     if(size<2) return 0;//假如棋盘分割到不足2*2则直接返回(这时候没办法用模板去覆盖棋盘了) 
26     int s;
27     s=size/2;//表示即将被再次分割后的棋盘大小 
28     if(dr<(tr+s)&&dc<(tc+s))//表示对当前输入的棋盘而言,残缺块在左上角部分的子棋盘 
29     {
30         cover(tr,tc,s,dr,dc);//对左上角子棋盘分割 
31         count++;
32         bord[tr+s-1][tc+s]=count;//下面三个赋值语句是用①号模板覆盖 
33         bord[tr+s][tc+s-1]=count;
34         bord[tr+s][tc+s]=count;
35         cover(tr,tc+s,s,tr+s-1,tc+s);//对右上角子棋盘分割
36         cover(tr+s,tc,s,tr+s,tc+s-1);//对左下角子棋盘分割
37         cover(tr+s,tc+s,s,tr+s,tc+s);//对右下角子棋盘分割
38     }
39     else if(dr<(tr+s)&&dc>=(tc+s))//表示对当前输入的棋盘而言,残缺块在右上角部分的子棋盘 
40     {
41         cover(tr,tc+s,s,dr,dc);//对右上角子棋盘分割
42         count++;
43         bord[tr+s-1][tc+s-1]=count;//下面三个赋值语句是用②号模板覆盖 
44         bord[tr+s][tc+s-1]=count;
45         bord[tr+s][tc+s]=count;
46         cover(tr,tc,s,tr+s-1,tc+s-1);//对左上角子棋盘分割
47         cover(tr+s,tc,s,tr+s,tc+s-1);//对左下角子棋盘分割
48         cover(tr+s,tc+s,s,tr+s,tc+s);//对右下角子棋盘分割
49     }
50     else if(dr>=(tr+s)&&dc<(tc+s))//表示对当前输入的棋盘而言,残缺块在左下角部分的子棋盘 
51     {
52         cover(tr+s,tc,s,dr,dc);
53         count++;
54         bord[tr+s-1][tc+s-1]=count;//下面三个赋值语句是用③号模板覆盖 
55         bord[tr+s-1][tc+s]=count;
56         bord[tr+s][tc+s]=count;
57         cover(tr,tc,s,tr+s-1,tc+s-1);
58         cover(tr,tc+s,s,tr+s-1,tc+s);
59         cover(tr+s,tc+s,s,tr+s,tc+s);
60     }
61     else if(dr>=(tr+s)&&dc>=(tc+s))//表示对当前输入的棋盘而言,残缺块在右下角部分的子棋盘 
62     {
63         cover(tr+s,tc+s,s,dr,dc);
64         count++;
65         bord[tr+s-1][tc+s-1]=count;//下面三个赋值语句是用④号模板覆盖 
66         bord[tr+s-1][tc+s]=count;
67         bord[tr+s][tc+s-1]=count;
68         cover(tr,tc,s,tr+s-1,tc+s-1);
69         cover(tr,tc+s,s,tr+s-1,tc+s);
70         cover(tr+s,tc,s,tr+s,tc+s-1);
71     }
72 }

 

策略b:

每一次分割时,在递归进入下一层之前,先填充伪残缺块。(因为这个时候已经根据残缺块坐标推出可以使用哪种模板填充了。)(这个时候size=n/2)
然后依次处理当前层的四个子棋盘:分割——填充伪残缺块——处理更小的子棋盘。
整个程序重复该过程,最终完成填充任务。

关键代码如下:

完整代码:

 1 #include<stdio.h>
 2 int count=0,bord[100][100];
 3 //覆盖以(tr,tc)为左上角坐标,宽为size的棋盘。残缺块或伪残缺块坐标为(dr,dc) 
 4 int cover(int tr,int tc,int size,int dr,int dc);
 5 int main()
 6 {
 7     int x,y,k,size=1;
 8     int i,j;
 9     scanf("%d%d%d",&k,&x,&y);
10     for(i=1;i<=k;i++) size=size*2;//计算2^k 
11     cover(0,0,size,x,y);//对原始棋盘进行覆盖 
12     for(i=0;i<size;i++)//输出棋盘的覆盖结果 
13     {
14         for(j=0;j<size;j++)
15         {
16             printf("%-2d ",bord[i][j]);
17         }
18         printf("\\n");
19     }
20     return 0;
21 }
22 
23 int cover(int tr,int tc,int size,int dr,int dc)
24 {
25     if(size<2) return 0;//假如棋盘分割到不足2*2则直接返回(这时候没办法用模板去覆盖棋盘了) 
26     int s;
27     s=size/2;//表示即将被再次分割后的棋盘大小 
28     if(dr<(tr+s)&&dc<(tc+s))//表示对当前输入的棋盘而言,残缺块在左上角部分的子棋盘 
29     {
30         count++;
31         bord[tr+s-1][tc+s]=count;//下面三个赋值语句是用①号模板覆盖 
32         bord[tr+s][tc+s-1]=count;
33         bord[tr+s][tc+s]=count;
34         cover(tr,tc,s,dr,dc);//对左上角子棋盘分割 
35         
36         cover(tr,tc+s,s,tr+s-1,tc+s);//对右上角子棋盘分割
37         cover(tr+s,tc,s,tr+s,tc+s-1);//对左下角子棋盘分割
38         cover(tr+s,tc+s,s,tr+s,tc+s);//对右下角子棋盘分割
39     }
40     else if(dr<(tr+s)&&dc>=(tc+s))//表示对当前输入的棋盘而言,残缺块在右上角部分的子棋盘 
41     {
42         count++;
43         bord[tr+s-1][tc+s-1]=count;//下面三个赋值语句是用②号模板覆盖 
44         bord[tr+s][tc+s-1]=count;
45         bord[tr+s][tc+s]=count;
46         cover(tr,tc+s,s,dr,dc);//对右上角子棋盘分割
47         
48         cover(tr,tc,s,tr+s-1,tc+s-1);//对左上角子棋盘分割
49         cover(tr+s,tc,s,tr+s,tc+s-1);//对左下角子棋盘分割
50         cover(tr+s,tc+s,s,tr+s,tc+s);//对右下角子棋盘分割
51     }
52     else if(dr>=(tr+s)&&dc<(tc+s))//表示对当前输入的棋盘而言,残缺块在左下角部分的子棋盘 
53     {
54         count++;
55         bord[tr+s-1][tc+s-1]=count;//下面三个赋值语句是用③号模板覆盖 
56         bord[tr+s-1][tc+s]=count;
57         bord[tr+s][tc+s]=count;
58         cover(tr+s,tc,s,dr,dc);
59         
60         cover(tr,tc,s,tr+s-1,tc+s-1);
61         cover(tr,tc+s,s,tr+s-1,tc+s);
62         cover(tr+s,tc+s,s,tr+s,tc+s);
63     }
64     else if(dr>=(tr+s)&&dc>=(tc+s))//表示对当前输入的棋盘而言,残缺块在右下角部分的子棋盘 
65     {
66         count++;
67         bord[tr+s-1][tc+s-1]=count;//下面三个赋值语句是用④号模板覆盖 
68         bord[tr+s-1][tc+s]=count;
69         bord[tr+s][tc+s-1]=count;
70         cover(tr+s,tc+s,s,dr,dc);
71         
72         cover(tr,tc,s,tr+s-1,tc+s-1);
73         cover(tr,tc+s,s,tr+s-1,tc+s);
74         cover(tr+s,tc,s,tr+s,tc+s-1);
75     }
76 }

 

策略c:

先不停地分割,当分割到不能再分割时依次填充四个size=2的子棋盘,然后回溯到size=4的棋盘填充伪残缺块。
然后回溯到size=8,重复刚才的过程,依次处理另外三个size=4的子棋盘。

关键代码如下:

 

完整代码:

 1 #include <iostream>
 2 #include <iomanip>
 3 
 4 using namespace std;
 5 
 6 int num=1,arr[100][100];
 7 void Cover(int tr,int tc,int size,int dr,int dc)
 8 {
 9     if(size<2) return ;
10     int s;
11     s=size/2;
12     if(dr<(tr+s)&&dc<(tc+s))
13     {
14         Cover(tr,tc,s,dr,dc);
15         Cover(tr,tc+s,s,tr+s-1,tc+s);
16         Cover(tr+s,tc,s,tr+s,tc+s-1);
17         Cover(tr+s,tc+s,s,tr+s,tc+s);
18         arr[tr+s-1][tc+s]=num;
19         arr[tr+s][tc+s-1]=num;
20         arr[tr+s][tc+s]=num;
21         num++;
22     }
23     else if(dr<(tr+s)&&dc>=(tc+s))
24     {
25         Cover(tr,tc,s,tr+s-1,tc+s-1);
26         Cover(tr,tc+s,s,dr,dc);
27         Cover(tr+s,tc,s,tr+s,tc+s-1);
28         Cover(tr+s,tc+s,s,tr+s,tc+s);
29         arr[tr+s-1][tc+s-1]=num;
30         arr[tr+s][tc+s-1]=num;
31         arr[tr+s][tc+s]=num;
32         num++;
33     }
34     else if(dr>=(tr+s)&&dc<(tc+s))
35     {
36         Cover(tr,tc,s,tr+s-1,tc+s-1);
37         Cover(tr,tc+s,s,tr+s-1,tc+s);
38         Cover(tr+s,tc,s,dr,dc);
39         Cover(tr+s,tc+s,s,tr+s,tc+s);
40         arr[tr+s-1][tc+s-1]=num;
41         arr[tr+s-1][tc+s]=num;
42         arr[tr+s][tc+s]=num;
43         num++;
44     }
45     else
46     {
47         Cover(tr,tc,s,tr+s-1,tc+s-1);
48         Cover(tr,tc+s,s,tr+s-1,tc+s);
49         Cover(tr+s,tc,s,tr+s,tc+s-1);
50         Cover(tr+s,tc+s,s,dr,dc);
51         arr[tr+s-1][tc+s-1]=num;
52         arr[tr+s-1][tc+s]=num;
53         arr[tr+s][tc+s-1]=num;
54         num++;
55     }
56 }
57 
58 int main()
59 {
60     int k,x,y;
61     int temp=1;
62     cin>>k>>x>>y;
63     arr[x][y]=0;
64     for(int i=0;i<k;i++)
65         temp=temp*2;
66     Cover(0,0,temp,x,y);
67     for(int a=0;a<temp;a++)
68     {
69         for(int b=0;b<temp;b++)
70         {
71             cout<<setw(3)<<arr[a][b];
72         }
73         cout<<endl;
74     }
75     return 0;
76 }

 

本问题可以参考:http://www.cnblogs.com/kahreman/archive/2011/08/08/2130613.html

以上是关于残缺棋盘的覆盖问题的主要内容,如果未能解决你的问题,请参考以下文章

棋盘覆盖问题——分治法——代码清晰易懂

分治(详解残缺棋盘 —— Java代码实现)

棋盘覆盖

分治法解决残缺棋盘

算法导论分治思想—大数乘法矩阵相乘残缺棋盘

棋盘覆盖问题与可视化代码演示