回朔法
参考博客 ,感谢博主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;
}
}