uva 1603 如何生成所有正方形,详细题解;

uva 1603:
这题比较难得是怎么存储所有的正方形;

我的方法:

以边的长度来决定正方形的大小; 首先遍历所有的边长:

//len :正方形的边长   n: 题目所给的最大边长;
for(int len = 1; len <= n; i++){
}

当n = k;会组成 由K * K的小正方形组成的大正方形;
在这里插入图片描述
这是个 n = 3的大正方形;
可以从小正方形出现遍历每一个边长为“len”的正方形;

//遍历每一个小正方形
for(int i = 0; i < n; i++){
	for(int j = 0; j < n; j++){
		
	}
}
//遍历每一个边长为“len”的正方形
for(int len = 1; len <= n; i++){
	for(int i = 0; i < n; i++){
		for(int j = 0; j < n; j++){
			
		}
	}
}

解决了遍历问题,还需要解决如何确定每一个边的位置;
在题目中,已经给每个火柴棍编号了(1 到 2n(n + 1) );
在这里插入图片描述
第 i 行第 j 列的边长为 1 的正方形的各边:

  1. 上边: (i - 1) * (2 * n + 1) + j + len * (2 * n + 1);//len = 0时, 就是上边

  2. 下边:(i - 1) * (2 * n + 1) + j + len * (2 * n + 1);

  3. 左边:(i - 1) * (2 * n + 1) + j + len + n; //len = 0时, 就是左边;

  4. 右边:(i - 1) * (2 * n + 1) + j + len + n;

读者不妨将值代入验算一下

//将上边与下边合成一个函数
int get_ud(int i, int j, int len) {
	return (i - 1) * (2 * n + 1) + j + len * (2 * n + 1);
}
//将左边与右边合成一个函数
int get_lr(int i, int j, int len) {
	return (i - 1) * (2 * n + 1) + j + len + n;
}
for(int len = 1; len <= 1; i++){
	for(int i = 0; i < n; i++){
		for(int j = 0; j < n; j++){
			int up = get_ud(i, j, 0);
			int down = get_ud(i, j, len);
			int left = get_lr(i, j, 0);
			int right = get_lr(i, j, len);
			这里将正方形的各边保存下来;
			
			//
			
		}
	}
}
//结束后求出了所有边长为 1 的正方形;

当边长大于 1 时,比较麻烦:

for (int len = 1; len <= n; len++) {
			for (int i = 1; i <= n; i++) {
				for (int j = 1; j <= n; j++) {
					if (i + len - 1 > n || j + len - 1 > n)//边长不够组成正方形了,退出
						break;
					for (int k = 0; k < len; k++) {//生成所有的边。  分len次生成,每次生成4条边,  总共 4 * len条边;
						int up = get_ud(i, j + k, 0);
						int down = get_ud(i, j + k, len);
						int left = get_lr(i + k, j, 0);
						int right = get_lr(i + k, j, len);
					}
					nums++;
				}
			}
		}

以上面那个图为例, 生成 2 * 2 的正方形:
第一次生成:1,4,6,15
第二次生成:2,11,16,13

之后就比较简单了, 保存二份所有的正方形的周长,一份不动,一份用于dfs()的遍历; 然后dfs()深度搜索,每次拿掉一个完整正方形的一根火柴(是否完整取决于该正方形的当前周长是否与他本来的周长相等),将缺少火柴的正方形周长减一;直到没有完整的正方形,算法结束;

坑:

	memset()是以字节为单位进行初始化, 不可以初始化1;
	IDA*算法,当数据量小时,可以不使用估价函数;只会减少一部分时间;
if (!get_legal_()) {
		//cout << d << endl;
		//maxd = d;
		return true;
	}
	if (d >= maxd)   return false;

这种情况,“d >= maxd”;用">=";

if (d >= maxd)   return false;
if (!get_legal_()) {
		//cout << d << endl;
		//maxd = d;
		return true;
	}

这种情况,“d > maxd”;用">";

#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
#include<bitset>
#include<sstream>
using namespace std;
int maxn = 5, n, m, nums, maxd;
int arr[51][61], cur_size[61], full_size[61];

int get_sticks() {
	return 2 * n * (n + 1);
}
int get_ud(int i, int j, int len) {
	return (i - 1) * (2 * n + 1) + j + len * (2 * n + 1);
}

int get_lr(int i, int j, int len) {
	return (i - 1) * (2 * n + 1) + j + len + n;
}

int get_legal_() {
	for (int i = 1; i < nums; i++) {
		//cout << cur_size[i] << "  " << full_size[i] << endl;
		if (cur_size[i] == full_size[i])
			return i;
	}
	return 0;
}
int out;
//bool dfs(int d) {
/* 自己思考的
void dfs(int d) {
	if (d >= maxd) return;
	int pos = get_legal_();
	//if (!pos) return true; 	//成功条件
	if (!pos) {
		out = d;
		return;
	}
	//if (d >= maxd) return false; 	//到达深度
	//if ((maxd - d) * n - nums > 0) return false; 	//IDA*启发函数
	for (int i = 1; i <= get_sticks(); i++) {//n种接下来的走法
		if (arr[pos][i]) {
			for (int j = 1; j < nums; j++)
				if (arr[j][i])
					cur_size[j]--;
			//if(dfs(d + 1)) return true;
			dfs(d + 1);
			for (int j = 1; j < nums; j++)
				if (arr[j][i])
					cur_size[j]++;
		}
	}
	return ;
}
*/
bool dfs(int d) {
	if (!get_legal_()) {
		//cout << d << endl;
		//maxd = d;
		return true;
	}
	if (d >= maxd)   return false;

	int k = get_legal_();  //选择一个正方形拿掉其中边上一根火柴
	for (int i = 1; i <= get_sticks(); i++) {
		if (arr[k][i]) {
			for (int j = 1; j < nums; j++)
				if (arr[j][i])   cur_size[j]--;

			if (dfs(d + 1)) return true;

			for (int j = 1; j < nums; j++)
				if (arr[j][i])   cur_size[j]++;
		}
	}
	return false;
}

void solve() {
	for (maxd = 1;; maxd++) {
		if (dfs(0)) {
			cout << maxd << endl;
			break;
		}
	}
	//maxd = n * n;
	//cout << maxd;
	//dfs(0);
	//cout << maxd << endl;
	return;
}
int main() {
	int t;
	int remov[61];
	cin >> t;
	while (t--) {
		cin >> n >> m;
		for (int i = 1; i <= get_sticks(); i++) remov[i] = 1;
		//cout << remov[24] << endl;
		//memset(arr, 0, sizeof(arr));
		for (int i = 0; i < m; i++) {
			int pos;
			cin >> pos;
			remov[pos] = 0;
		}

		nums = 1;
		memset(arr, 0, sizeof(arr));
		for (int len = 1; len <= n; len++) {
			for (int i = 1; i <= n; i++) {
				for (int j = 1; j <= n; j++) {
					cur_size[nums] = 0;
					full_size[nums] = 4 * len;
					if (i + len - 1 > n || j + len - 1 > n)
						break;
					for (int k = 0; k < len; k++) {
						int up = get_ud(i, j + k, 0);
						int down = get_ud(i, j + k, len);
						int left = get_lr(i + k, j, 0);
						int right = get_lr(i + k, j, len);
						//cout << up << "  " << down << " " << left << "  " << right << "  ";
						arr[nums][up] = 1;
						arr[nums][down] = 1;
						arr[nums][left] = 1;
						arr[nums][right] = 1;
						//cout << remov[up] << " " << remov[down] << " " << remov[left] << " " << remov[right] << endl;
						cur_size[nums] += (remov[up] + remov[down] + remov[left] + remov[right]);
					}
					//cout << endl;
					nums++;
				}
			}
		}
		solve();
	}

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值