UVA1603Square Destroyer破坏正方形

题意:有一个由火柴棍组成的正方形网格,每条边有n根火柴,共2n(n+1)根。从上到下、从左到右给各个火柴编号。现在拿走一下火柴,问剩下的火柴中,至少还要拿走多少根火柴才能破坏正方形。

分析:列举每种可能,显然是回溯,思想就是每次考虑一个没有被破坏的正方形,都被破坏就退出并更新当前最优解,就是深度。这题难点就是如何表示正方形,先将所有正方形枚举出来,将它的四条边存入数组,而它边的表示可以算出来。

作者代码中有很多巧妙之处,细细体会收获很大,比如正方形的边存入。

#include<cstdio>
#include<cstring>
#include<cctype>
#include<queue>
#include<iostream>
#include<vector>
#include<list>
#include<set>
using namespace std;
const int maxn = 100;int n;
int existedge[maxn];
int contain[maxn][maxn];
int cursize[maxn],fullsize[maxn];
int allsquare = 0; int curbest;
inline int row_match(int x, int y) {
	return (2 * n + 1)*x + y;
}
inline int col_match(int x, int y) {
	return  (2 * n + 1)*x + n+y;
}
void init() {
	
	int m,v;
	cin >> n>>m;
	for (int i = 0; i < 2 * n*(n + 1); i++)existedge[i] = 1;
	for (int i = 0; i < m; i++) {
		cin >> v;
		existedge[v-1] = 0;
	}
	memset(contain, 0, sizeof(contain));
	allsquare = 0;
	for (int i = 1; i <= n; i++) {//枚举 边的数量
		for (int x = 0; x <= n-i; x++) {
			for (int y = 0; y <= n-i; y++) {
				cursize[allsquare] = 0;
				fullsize[allsquare] = 4 * i;//期望边数
				for (int e = 0; e < i; e++) {
					int a = row_match(x, y+e);//上边
					int b = row_match(x + i, y+e);//下边
					int c = col_match(x+e, y);//左边
					int d = col_match(x+e, y +i);//右边
					cursize[allsquare] += existedge[a] + existedge[b] + existedge[c] + existedge[d];//实际边数
					contain[allsquare][a] = 1;
					contain[allsquare][b] = 1;
					contain[allsquare][c] = 1;
					contain[allsquare][d] = 1;
				}
				++allsquare;
			}
		}
	}
}
int findsquare() {//找到未被破坏的正方形
	for (int i = 0; i < allsquare; i++) {
		if (cursize[i] == fullsize[i]) {
			return i;
		}
	}
	return -1;
}
void dfs(int cur) {
	if (cur >= curbest)return;//比当前最优值大,剪枝
	int k = findsquare();
	if (k == -1) {//没有完整正方体
		curbest = cur;
		return;
	}
	for (int i = 0; i < 2 * n*(n + 1); i++) {
		if (contain[k][i]) {
			for (int j = 0; j < allsquare; j++) {
				if (contain[j][i]) {//找与它相邻边的正方形
					cursize[j]--;//包含本身
				}                           //这里回溯之所以不考虑contain[j][i],不置0,是因为它少一条边之后就是破坏正方形,下次搜索不到
			}
			dfs(cur + 1);
			for (int j = 0; j < allsquare; j++) {
				if (contain[j][i]) {
					cursize[j]++;//包含本身
				}
			}
		}
	}

}
int main() {
	int kase = 0;
	cin >> kase;
	while (kase-- > 0) {
		init();
		curbest = n * n;
		dfs(0);
		cout << curbest << endl;
	}
	
	//system("pause");
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值