棋盘覆盖问题(分治)

问题描述:在一个 2 k ∗ 2 k ( k > 0 ) 2^k*2^k(k>0) 2k2k(k>0)的棋盘,只有一个与其它方格不同的特殊方格,现在要用下图所示的 L 形骨牌覆盖除了特殊方格外的其它全部方格,任意两骨牌不能重叠,给出一种覆盖方法。
在这里插入图片描述

本题可以将大棋盘划分为四个大小相同的象限,那么这个特殊方格必定存在其中一个象限,根据方格存在的不同象限,L 形骨牌的摆法:
在这里插入图片描述
这样便能使被划分的四个象限均存在一个“特殊的方格”,达到分治的条件。

分治一个最重要的特点在于一个复杂的大问题可以分解为几个性质或求解思路相同的小问题,且小问题更容易求解。

代码实现过程中需要创建一个和初始棋盘大小相同的二维数组用来保存棋盘每个方格覆盖骨牌的编号。

java代码

public class Solution {
	int tile; 												// 骨牌编号,从1开始
	int[][] board; 											// 与初始棋盘对应的二维数组

	public Solution(int size) { 							// 构造函数,初始化
		board = new int[size][size];
		tile = 1;
	}

	// (tr,tc)表示一个象限左上角方格的行列号,(dr,dc)表示特殊方格行列号,其中初始棋盘左上角行列号(0,0)
	public void chessBoard(int tr, int tc, int dr, int dc, int size) {
		if (size == 1) { 									// 递归出口
			return;
		}
		
		int t = tile++; 									// 取骨牌
		int s = size >> 1; 									// 分割棋盘
		// 考虑左上角象限
		if (dr < tr + s && dc < tc + s) { 					// 特殊方格在此象限处理此象限
			chessBoard(tr, tc, dr, dc, s);
		} else { 													// 特殊方格不在此象限
			board[tr + s - 1][tc + s - 1] = t; 						// t号骨牌覆盖象限最右下角
			chessBoard(tr, tc, tr + s - 1, tc + s - 1, s); 			// 将右下角视为特殊方格继续处理该象限
		}
		// 考虑右上角象限
		if (dr < tr + s && dc >= tc + s) {					// 特殊方格在此象限处理此象限
			chessBoard(tr, tc + s, dr, dc, s);
		} else { 													// 特殊方格不在此象限
			board[tr + s - 1][tc + s] = t; 							// t号骨牌覆盖象限最左下角
			chessBoard(tr, tc + s, tr + s - 1, tc + s, s); 			// 将左下角视为特殊方格继续处理该象限
		}
		// 考虑左下角象限
		if (dr >= tr + s && dc < tc + s) { 					// 特殊方格在此象限处理此象限
			chessBoard(tr + s, tc, dr, dc, s);
		} else { 													// 特殊方格不在此象限
			board[tr + s][tc + s - 1] = t; 							// t号骨牌覆盖象限最右上角
			chessBoard(tr + s, tc, tr + s, tc + s - 1, s); 			// 将右上角视为特殊方格继续处理该象限
		}
		// 考虑右下角象限
		if (dr >= tr + s && dc >= tc + s) { 				// 特殊方格在此象限处理此象限
			chessBoard(tr + s, tc + s, dr, dc, s);
		} else { 													// 特殊方格不在此象限
			board[tr + s][tc + s] = t; 								// t号骨牌覆盖象限最左上角
			chessBoard(tr + s, tc + s, tr + s, tc + s, s); 			// 将左上角视为特殊方格继续处理该象限
		}
	}

	public static void main(String[] args) {
		int k = 3;
		int size = 1 << k;
		Solution s = new Solution(size);
		int tr = 1;											// 特殊方格的行号
		int tc = 2;											// 特殊方格的列号
		s.board[tr][tc] = 0; 								// 特殊方格用0表示
		s.chessBoard(0, 0, tr, tc, size);
		// 打印覆盖结果
		for (int i = 0; i < size; i++) {
			for (int j = 0; j < size; j++) {
				System.out.print(s.board[i][j] + "\t");
			}
			System.out.println();
		}
	}	
}

运行结果(0表示特殊方格的位置,3个相同数字代表一块骨牌)
在这里插入图片描述

时间复杂度分析

T ( k ) T(k) T(k)表示求解一个 2 k ∗ 2 k 2^k*2^k 2k2k棋盘的时间

  • k = 1 k=1 k=1 T ( k ) = O ( 1 ) T(k)=Ο(1) T(k)=O(1)
  • k > 1 k>1 k>1 T ( k ) = 4 T ( k − 1 ) T(k)=4T(k-1) T(k)=4T(k1)

∴ T ( k ) = O ( 4 k ) \therefore T(k)=Ο(4^k) T(k)=O(4k)

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
棋盘覆盖问题分治算法的正确性可以通过数学归纳法来证明。首先,我们可以将棋盘分成四个大小相等的子棋盘,然后将L型骨牌放在其中一个子棋盘中心的方格上。接着,我们可以将问题转化为对剩余三个子棋盘的棋盘覆盖问题进行递归求解。由于每个子棋盘的大小都是原棋盘大小的1/2,因此递归求解的次数为log2n,其中n为原棋盘的大小。 在每一层递归中,我们都可以将棋盘分成四个大小相等的子棋盘,并将L型骨牌放在其中一个子棋盘中心的方格上。由于每个L型骨牌覆盖了一个黑色和一个白色的方格,因此每个子棋盘中心的方格必须是白色的。因此,我们可以将每个子棋盘中心的方格标记为白色,并将其余方格标记为黑色。这样,我们就可以将棋盘覆盖问题转化为对黑色方格的覆盖问题进行递归求解。 在递归求解的过程中,我们可以使用归纳法证明每个子棋盘都可以被完美地覆盖。首先,对于原棋盘的四个角落,它们都是黑色的,因此它们必须被覆盖。由于每个L型骨牌覆盖了一个黑色和一个白色的方格,因此我们可以将每个子棋盘中心的方格用一个L型骨牌覆盖,从而覆盖了四个角落。接着,我们可以使用归纳法假设每个大小为2k x 2k的子棋盘都可以被完美地覆盖,然后证明每个大小为2k+1 x 2k+1的子棋盘也可以被完美地覆盖。 对于一个大小为2k+1 x 2k+1的子棋盘,它可以被分成四个大小为2k x 2k的子棋盘和四个大小为2k x 1的矩形。由于每个矩形都包含一个黑色和一个白色的方格,因此它们必须被覆盖。由于每个大小为2k x 2k的子棋盘都可以被完美地覆盖,因此我们可以使用归纳假设将它们覆盖。接着,我们可以将四个L型骨牌放在四个大小为2k x 1的矩形中心的方格上,从而覆盖了整个子棋盘。 综上所述,棋盘覆盖问题分治算法的正确性可以通过数学归纳法来证明。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值