题目描述:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=51165
/*
solution:
可以用迭代加深算法作为框架。搜索对象主要有两种:一是正方形,而是火柴。
个人采取前者。
date:2016/5/15
*/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 60;
int s, n, k, rec_size[maxn], rec_full_size[maxn], contain[maxn][maxn]; //contain[i][j] = 1代表编号为i的正方形包含了编号为j的火柴
int sticks[maxn], maxd; //stick[i] = 1代表编号为i的火柴存在
inline int get_sticks() {
return 2 * n * (n + 1);
}
inline int get_horizental(int r, int c) {
return (r - 1) * (2 * n + 1) + c;
}
inline int get_vertical(int r, int c) {
return n * (2 * r - 1) + r + c - 1;
}
int get_rec_num() {
for(int i = 1; i < s; i++)
if(rec_size[i] == rec_full_size[i])
return i;
return 0;
}
void dfs(int d) {
if(d >= maxd) return;
if(!get_rec_num()) {
maxd = d;
return;
}
int k = get_rec_num(); //选择一个正方形拿掉其中边上一根火柴
for(int i = 1; i <= get_sticks(); i++) {
if(contain[k][i]) {
for(int j = 1; j < s; j++)
if(contain[j][i]) rec_size[j]--;
dfs(d + 1);
for(int j = 1; j < s; j++)
if(contain[j][i]) rec_size[j]++;
}
}
}
int main()
{
//freopen("input.txt", "r", stdin);
int T;
scanf("%d", &T);
while(T--) {
scanf("%d%d", &n, &k);
for(int i = 1; i <= get_sticks(); i++) sticks[i] = 1;
int v;
while(k--) {
scanf("%d", &v);
sticks[v] = 0;
}
//初始化,构建各个正方形
s = 1;
memset(contain, 0, sizeof(contain));
for(int len = 1; len <= n; len++) {
for(int i = 1; i <= n - len + 1; i++)
for(int j = 1; j <= n - len + 1; j++) {
rec_size[s] = 0;
rec_full_size[s] = 4 * len;
for(int k = 0; k < len; k++) {
int a = get_horizental(i, j + k); //上行
int b = get_horizental(i + len, j + k); //下行
int c = get_vertical(i + k, j); //左列
int d = get_vertical(i + k, j + len); //右列
contain[s][a] = 1;
contain[s][b] = 1;
contain[s][c] = 1;
contain[s][d] = 1;
rec_size[s] += (sticks[a] + sticks[b] + sticks[c] + sticks[d]);
}
s++;
}
}
//迭代加深
maxd = n * n;
dfs(0);
printf("%d\n", maxd);
}
return 0;
}