专栏文章

题解:P11047 [蓝桥杯 2024 省 Java B] LITS 游戏

P11047题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@miq1u43a
此快照首次捕获于
2025/12/03 21:34
3 个月前
此快照最后确认于
2025/12/03 21:34
3 个月前
查看原文

前置知识:精确覆盖问题

精确覆盖问题简单来说就是,给定 nnmm 列的 01 矩阵,选出若干行使得对于选出的行中,对于第 j[1,m]j \in [1, m] 列有且仅有某一行的第 jj 列为 1 。
舞蹈链是一种解决精确覆盖问题的实现方法,在实际应用中,我们通常把行当做选择,列当做限制。也就是说,我们有 nn 种选择,mm 条需要满足的限制。

本题思路

首先统计出值为 1 的格子数量 mm,我们让 k=m+4k = m + 4 其中 [1,m][1, m] 列为所有 1 的格子下标(因为每个值为 1 的格子属于 1 个或 0 个图形),[m+1,k][m + 1, k] 列为 4 个图形的下标(因为题目要求我们找出)。
然后枚举每个值为 1 的格子,查看如果以当前格子为起点,是否能摆出某个图形,如果可以,就把这个图案涉及到的几个格子都插入,再插入这个图形的列。否则,只插入当前格子自己的列。
建模完成后,我们使用舞蹈链模板求解即可。
CPP
#include <iostream>
#include <cstring>
using namespace std;
const int N = 55, M = 500000;
struct Pos {
	int x, y;
};
char arr[N][N];
int idle = 0;
int L[M], R[M], U[M], D[M], head[M];
int cnt[N * N];
Pos pos[M];
int n, m;
int dir[4][4][4][2] = {
	{ //图案
		{ //旋转
			{ 0, 0 }, { -1, 0 }, { -2, 0 }, { 0, 1 } //坐标偏移
		},
		{
			{ 0, 0 }, { 0, 1 }, { 0, 2 }, { 1, 0 }
		},
		{
			{ 0, 0 }, { 0, -1 }, { 1, 0 }, { 2, 0 }
		},
		{
			{ 0, 0 }, { -1, 0}, { 0, -1 }, { 0, -2 }
		}
	},
	{
		{
			{ 0, 0 }, { 0, 1 }, { 0, 2 }, { 0, 3 }
		},
		{
			{ 0, 0 }, { 1, 0 }, { 2, 0 }, { 3, 0 }
		},
		{
			{ 0, 0 }, { 0, 1 }, { 0, 2 }, { 0, 3 }
		},
		{
			{ 0, 0 }, { 1, 0 }, { 2, 0 }, { 3, 0 }
		}
	},
	{
		{
			{ 0, 0 }, { 0, 1 }, { 0, -1 }, { 1, 0 }
		},
		{
			{ 0, 0 }, { 0, -1 }, { 1, 0 }, { -1, 0 }
		},
		{
			{ 0, 0 }, { -1, 0 }, { 0, -1 }, { 0, 1 }
		},
		{
			{ 0, 0 }, { 0, 1 }, { -1, 0 }, { 1, 0 }
		}
	},
	{
		{
			{ 0, 0 }, { 0, 1 }, { -1, 1 }, { -1, 2 }
		},
		{
			{ 0, 0 }, { 1, 0 }, { 1, 1 }, { 2, 1 }
		},
		{
			{ 0, 0 }, { 0, -1 }, { 1, -1 }, { 1, -2 }
		},
		{
			{ 0, 0 }, { -1, 0 }, { -1, -1 }, { -2, -1 }
		}
	}
};
void insert(int row, int col) {
	idle++;
	cnt[col]++;
	pos[idle] = { row, col };
	U[idle] = U[col];
	D[U[col]] = idle;
	U[col] = idle;
	D[idle] = col;
	if (!head[row]) head[row] = L[idle] = R[idle] = idle;
	else {
		L[idle] = L[head[row]];
		R[L[head[row]]] = idle;
		L[head[row]] = idle;
		R[idle] = head[row];
	}
}
int ind[N][N] = { 0 }; //为每个 '1' 分配列
void build() {
	memset(head, 0, sizeof head);
	memset(cnt, 0, sizeof cnt);
	memset(ind, 0, sizeof ind);
	m = 0;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			if (arr[i][j] == '1')
				m++, ind[i][j] = m;
	m += 4; //为四个图形分配列
	for (int i = 0; i <= m; i++) {
		L[i] = i - 1, R[i] = i + 1;
		U[i] = D[i] = i;
		pos[i] = { 0, i };
	}
	idle = m;
	L[0] = m, R[m] = 0;
	int r = 0;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			if (arr[i][j] == '1') {
				for (int t = 0; t < 4; t++) //遍历每个图案
					for (int d = 0; d < 4; d++) { //遍历每个角度
						bool f = true;
						for (int c = 0; c < 4; c++) { //查看每个偏移
							int dx = i + dir[t][d][c][0], dy = j + dir[t][d][c][1];
							if (dx < 1 || dy < 1 || dx > n || dy > n || arr[dx][dy] != '1') {
								f = false;
								break;
							}
						}
						if (f) {
							r++;
							insert(r, m - 3 + t);
							for (int c = 0; c < 4; c++) {
								int dx = i + dir[t][d][c][0], dy = j + dir[t][d][c][1];
								insert(r, ind[dx][dy]);
							}
						}
					}
				r++;
				insert(r, ind[i][j]);
			}
}
//冻结第 i 列及其关联行
void freeze(int col) {
	L[R[col]] = L[col], R[L[col]] = R[col];
	for (int i = D[col]; i != col; i = D[i])
		for (int j = R[i]; j != i; j = R[j])
			cnt[pos[j].y] --, U[D[j]] = U[j], D[U[j]] = D[j];
}
//激活第 i 列及其关联行
void active(int col) {
	L[R[col]] = R[L[col]] = col;
	for (int i = U[col]; i != col; i = U[i])
		for (int j = L[i]; j != i; j = L[j])
			cnt[pos[j].y] ++, U[D[j]] = j, D[U[j]] = j;
}
bool dance() {
	if (!R[0]) return true;
	int s = R[0];
	for (int i = R[s]; i; i = R[i])
		if (cnt[i] < cnt[s])
			s = i;
	freeze(s);
	for (int i = D[s]; i != s; i = D[i]) {
		for (int j = R[i]; j != i; j = R[j])
			freeze(pos[j].y);
		if (dance()) return true;
		for (int j = L[i]; j != i; j = L[j])
			active(pos[j].y);
	}
	active(s);
	return false;
}
int main() {
	int _;
	scanf("%d", &_);
	while (_--) {
		scanf("%d", &n);
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
				scanf(" %c", &arr[i][j]);
		build();
		if (dance()) puts("Yes");
		else puts("No");
	}
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...