Triomino拼图 || 棋盘覆盖问题 (分治法)

Triomino拼图是有一个2^k * 2^k (k>=1)个方格的棋盘,其中恰有一个方格残缺。下图给出了k=1时各种可能的Triomino拼图,其中残缺方格用空白表示。

这样的棋盘被称作“三格板”,Triomino拼图问题就是要用这种三格板覆盖更大的Triomino拼图。

要求:(1)两个三格板不能重叠;

        (2)三格板不能覆盖残缺方格,但必须覆盖其他所有方格。

问题分析:

Triomino 拼图问题是关于如何将一系列特定的L形拼图块排列进一个预定大小的矩形框架中,要求完全填充这个框架且没有剩余空间。

数学建模:

我们可以将拼图问题抽象为一个矩阵填充问题,其中每个L形拼图块需要被放置在矩阵的特定位置上,以满足全部拼图块都能完美拟合进矩阵的约束。该问题可以用一个二维数组表示,数组中的每个元素代表矩阵中的一个单元格,可以是空的或者被某个拼图块所占据。

算法设计:

首先把棋盘划分为四块,依次考虑左上,右上,左下,右下。如果缺陷位置在左上,则继续递归,再分划成四块,只到每块的大小为1,如果缺陷位置不在左上,则填充最右下的坐标,然后继续分划。另一种顺序是,先找到缺陷位置所在象限,然后再划分棋盘,再填充。

C语言代码如下:

#include<stdio.h>
#define N 100
int board[N][N];
int tile = 0;
void ChessBoard(int tr, int tc, int dr, int dc, int size);
int main() {
	int tx = 0, ty = 0, dx = 1, dy = 0;
	int n = 4;
	printf("请输入棋盘的规模(2^k):n=");
	scanf("%d", &n);
	printf("请输入缺陷处坐标(dx,dy)(从0开始):");
	scanf("%d %d", &dx, &dy);
	board[dx][ dy] = -1;
	ChessBoard(tx, ty, dx, dy, n);
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			printf("%4d", board[i][j]);
		}
		printf("\n\n");
	}
	putchar('\n'); 
	return 0;
}

void ChessBoard(int tx, int ty, int dx, int dy, int n) {
	if (n == 1) return;
	
	int t = tile++;//L型骨牌号
	int s = n / 2;//分割棋盘
	//覆盖左上角棋盘
	if (dx < tx + s && dy < ty + s) {//有特殊方格则继续划分
		ChessBoard(tx, ty, dx, dy, s);
	}
	else {//无特殊方格则先覆盖再划分
		board[tx + s - 1][ty + s - 1] = t;//覆盖右下角
		ChessBoard(tx, ty, tx + s - 1, ty + s - 1, s);
	}
	//覆盖右上角棋盘
	if (dx < tx + s && dy >= ty + s) {//有特殊方格则继续划分
		ChessBoard(tx, ty+s, dx, dy, s);
	}
	else {
		board[tx + s - 1][ty + s] = t;//覆盖左下角
		ChessBoard(tx, ty+s, tx + s - 1, ty + s, s);
	}
	//覆盖左下角棋盘
	if (dx >= tx + s && dy < ty + s) {//有特殊方格则继续划分
		ChessBoard(tx+s, ty, dx, dy, s);
	}
	else {
		board[tx + s ][ty + s - 1] = t;//覆盖右上角
		ChessBoard(tx +s, ty,tx+s, ty + s - 1, s);
	}
	//覆盖右下角棋盘
	if (dx >= tx + s && dy >= ty + s) {//有特殊方格则继续划分
		ChessBoard(tx+s, ty+s, dx, dy, s);
	}
	else {
		board[tx + s][ty + s] = t;//覆盖左上角
		ChessBoard(tx+s, ty + s, tx + s , ty + s, s);
	}
}

代码看不懂的可以去mooc看东北大学郭楠教授的算法设计与分析课,讲的很清楚,易懂。

  • 23
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值