Square Destroyer UVA - 1603 

回朔法

参考博客 ,感谢博主https://www.cnblogs.com/zyb993963526/p/6349947.html#commentform

https://vjudge.net/problem/UVA-1603

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#include<set>
#pragma warning(disable:4996)
using namespace std;
const int MAXN = 60;
int exist[MAXN];
int Size[MAXN], fullsize[MAXN], contains[MAXN][MAXN];
int n, k, s, best;
int cal_max_squere_number(int n)  //计算长度为n的正方形包含的所有正方形个数
{
	return (n + 1) * 2 * n;
}
int row(int x, int y)
{
	return (2 * n + 1)*x + y;
}
int col(int x, int y)
{
	return (2 * n + 1)*x + n + y;
}
void init()
{
	//虽然已经选定了用回溯法,并选择对正方形进行搜索,但是数据表示是个难点
	//我们对正方形进行搜索其实就是找那些还没有被破坏的正方形,然后再去一个一个的试着去拆除这些正方形的边
	//明确了任务,那就来考虑数据表示,首先怎么判断一个正方形有没有被破坏
	//我们检查这个正方形的边的条数,对于长度为n的正方形,边数小于n方就说明被破坏
	//那么我们就要去存储每个正方形未被破坏时的边数,还要去存储当前每个正方形的边数
	//显然要用数组存,那么我们就要对每个正方形进行编号,方法自然是枚举每个正方形,然后统计正方形边数
	//然后再考虑下一个,我们要拆除正方形的边
	//很显然我们要知道正方形有哪些边是存在的,题目中对边已经进行了编号了,那么我们只需要用一个二维数组存储就行了
	//另外还可以用一个辅助数组,直接判断哪些边存在,哪些边不存在
	//下面的代码中
	//exist数组哪些边存在,哪些边不存在,size[k]表示标号为k的正方形的当前边的数量
	//full[k]表示标号为k的正方形没被破坏时的边数
	//contains[i][j]=1表示编号为i的正方形的标号为j的边存在,反之不存在

	//这道题给了我许多启发,确定这道题用什么方法很简单,使用迭代加深算法,很容易想到,具体怎么搜索也很容易想到
	//但是这道题并没有完,因为我们还没有去考虑选择什么数据结构,而恰恰是数据结构卡住了我
	//由此可见,数据结构与算法息息相关,我们在平时的学习过程之中,一定要重视如何表示数剧
	//既要学习如何去做,更要知道拿什么去做,这样学习才是正确的
	scanf("%d%d", &n, &k);
	for (int i = 0; i < cal_max_squere_number(n); i++) exist[i] = 1;
	while (k--) {
		int tmp;
		scanf("%d", &tmp); exist[tmp - 1] = 0;
	}
	memset(Size, 0, sizeof(Size));
	memset(contains, 0, sizeof(contains));
	s = 0;          //用于给正方形编号
	for (int i = 1; i <= n; i++)          //枚举正方形长度
		for (int x = 0; x <= n - i; x++) //枚举左上角x坐标
			for (int y = 0; y <= n - i; y++) {//同理
				fullsize[s] = 4 * i;          //计算正方形长度
				for (int k = 0; k < i; k++)
				{
					int a = row(x, y + k);      //根据坐标计算编号
					int b = row(x + i, y + k);
					int c = col(x + k, y);
					int d = col(x + k, y + i);
					contains[s][a] = contains[s][b] = contains[s][c] = contains[s][d] = 1;
					Size[s] += exist[a] + exist[b] + exist[c] + exist[d];
				}
				s++;
			}


}
int find_square()
{
	for (int i = 0; i < s; i++)
		if (fullsize[i] == Size[i])     return i; //找到未被破坏的正方形
	return -1;
}

void dfs(int cur)
{
	if (cur >= best) return; //剪枝
	int k = find_square();
	if (k == -1) {
		best = cur;
		return;
	}
	for (int i = 0; i < cal_max_squere_number(n); i++)
	{
		if (contains[k][i]) {
			for (int j = 0; j < s; j++)
				if (contains[j][i]) Size[j]--;
			dfs(cur + 1);
			for (int j = 0; j < s; j++)
				if (contains[j][i]) Size[j]++;
		}
	}
	return;
}
int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		init();
		best = n * n;
		dfs(0);
		cout << best << endl;
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值