题意:有一个由火柴棍组成的正方形网格,每条边有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;
}