残缺棋盘的覆盖问题

棋盘覆盖问题   

问题描述:

在一个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

时间: 2024-10-14 08:36:41

残缺棋盘的覆盖问题的相关文章

残缺棋盘

//残缺棋盘的问题要求用3个方格的板(三格板)(triominoes)覆盖残缺棋盘. //在此覆盖中,两个三格板不能重叠,三格板不能覆盖残缺方格,但必须覆盖其他所有的方格. //在这种限制条件下,所需要的三格板总数为(2^(2k-1))/3. //可以验证(2^(2k-1))/3是一个整数.k为0的残缺棋盘很容易被覆盖, //因为它没有非残缺的方格,用于覆盖的三格板的数目为0.当k=1时, //正好存在3个非残缺的方格,并且这三个方格某一方 向的三格板来覆盖. //小残缺棋盘 //  1 //1

hihoCoder #1162 : 骨牌覆盖问题&#183;三

#1162 : 骨牌覆盖问题·三 Time Limit:10000ms Case Time Limit:1000ms Memory Limit:256MB 描述 前两周里,我们讲解了2xN,3xN骨牌覆盖的问题,并且引入了两种不同的递推方法.这一次我们再加强一次题目,对于给定的K和N,我们需要去求KxN棋盘的覆盖方案数. 提示:KxN骨牌覆盖 输入 第1行:2个整数N.表示棋盘宽度为k,长度为N.2≤K≤7,1≤N≤100,000,000 输出 第1行:1个整数,表示覆盖方案数 MOD 1235

骨牌覆盖

P2070 - 骨牌覆盖 Description 有一个3*n的棋盘让你放入若干1*2的骨牌,要求将整棋盘恰好覆盖满.求方案数! Input 一个整数n. Output 方案数模12357的值. Sample Input 2 Sample Output 3 Hint 1<=n<=100000000 td p { margin-bottom: 0cm } pre.cjk { font-family: "Droid Sans Fallback", monospace } p {

P2070 - 骨牌覆盖

Description 有一个3*n的棋盘让你放入若干1*2的骨牌,要求将整棋盘恰好覆盖满.求方案数! Input 一个整数n. Output 方案数模12357的值. Sample Input 2 Sample Output 3 Hint 1<=n<=100000000 Source 递推,矩阵快速幂 这题先通过打表找出递推规律,然后推出转移矩阵,使用矩阵快速幂即可. 1 #include<algorithm> 2 #include<iostream> 3 #incl

nyoj 328 完全覆盖

完全覆盖 时间限制:2000 ms  |  内存限制:65535 KB 难度:3 描述 有一天小董子在玩一种游戏----用2*1或1*2的骨牌把m*n的棋盘完全覆盖.但他感觉游戏过于简单,于是就随机生成了两个方块的位置(可能相同),标记一下,标记后的方块不用覆盖.还要注意小董子只有在m*n的棋盘能被完全覆盖后才会进行标记.现在他想知道:如果标记前m*n的棋盘能被完全覆盖,标记后的棋盘是否能被完全覆盖? 输入 第一行有一个整数t(1<=t<=100000),表示有t组测试数据.每组测试数据有三行

《程序员的数学思维修炼》 读书笔记

电子书定价:     ¥ 45.00       这是什么?                     纸书定价:     ¥ 45.00       Kindle电子书价格:     ¥ 1.99                   为您节省:     ¥ 43.01      (0.4折)            ~ 周颖   等 (作者) 发售日期: 2014年4月1日 本书是一本专门为程序员而写的数学书,介绍了程序设计中常用的数学知识.本书门槛不高,不需要读者精通很多高深的数学知识,只需要读

HDU 5245 Joyful (2015年上海大都赛J题,概率)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5245 题意: 给定一个n*m的矩形,由n*m个格子组成,我们可以选k次,每次可以选择的两个格子 这两个格子作为矩形的对角线可以确定一个矩形,这个矩形里的所有小格子都会被覆 盖,求k次后,被覆盖的格子的个数的期望. 分析: 棋盘被覆盖的格子数的期望 = 每个格子被覆盖的概率的和. 每次选择的方案有n*m*n*m种. 格子坐标为(i,j)被覆盖的方案数为: tot = 2*(2*(i*j*(n-i+1)

算法设计与分析笔记(一)递归与分治策略

一>递归:直接或间接地调用自身的算法. EG: 1>阶乘定义 n!=n(n-1)! (n>0); pubic static int factorial(int n ){ if(n==0) return 1; else return n*factorial(n-1); } 2>FiBonacci数列 public static int fibo(int n){ if(n<=1)return 1; else fibo(n-1)+fibo(n-2); } 3>排列问题(一个集

Fibonacci数列性质的组合证明

数列 1, 1, 2, 3, 5, 8, 13, 21, 34, … 叫做 Fibonacci 数列.这个数列有很多神奇的性质,其中一个性质是,每一个 Fibonacci 数的平方与它前后两个 Fibonacci 数的乘积相比一定正好相差 1 .具体地说,如果把第 n 个 Fibonacci 数记做 Fn ,那么有: Fn+1 · Fn+1 - Fn · Fn+2 = (-1)n Fibonacci 数有很多组合数学上的意义.比如说,用 1 × 1 和 1 × 2 的积木覆盖一个 1 × n 的