#include<cstdio>#include<cstring>#include<vector>usingnamespace std;constint N =61;// 网格最大是 5 * 5 的,其中最多会有 5 * (5 + 1) * 2 = 60 个正方形,所以要开到 61int n, idx;// n 为网格规模,idx 为正方形数量int max_depth;// IDA* 的 max_depth
vector<int> square[N];// 存每个正方形边上的火柴的编号bool used[N];// 存每个火柴是否已经被破坏// 新加一个左上角坐标为 (r, c),边长为 len 的正方形voidadd(int r,int c,int len){
int d = n <<1|1;// 由于用到的 2n + 1 比较多,这里先用一个变量代替掉 2n + 1vector<int>&s = square[idx];s.clear();// 有多组测试数据,需要上一组数据的内容清空for(int i =0; i < len; i ++){
s.push_back(1+ r * d + c + i);// 上边第 i 个s.push_back(1+(r + len)* d + c + i);// 下边第 i 个s.push_back(1+ n + r * d + c + i * d);// 左边第 i 个s.push_back(1+ n + r * d + c + i * d + len);// 右边第 i 个}idx ++;}// 判断正方形 s 是否完整boolcheck(vector<int>&s){
for(int i =0; i <(int)s.size(); i ++)if(used[s[i]])returnfalse;// 如果其中有一条边已经被破坏了,那么说明不完整returntrue;// 如果每条边都没被破坏,说明完整}// 估价函数intf(){
staticbool backup[N];// 由于要改动 used,需要先新建一个备份数组memcpy(backup, used,sizeof used);// 将 used 复制到备份数组中int res =0;for(int i =0; i < idx; i ++)// 枚举所有正方形if(check(square[i]))// 如果某个正方形是完整的,{
res ++;// 那么 res ++ ,并将该正方形所有的边都删去for(int j =0; j <(int)square[i].size(); j ++)used[square[i][j]]=true;}memcpy(used, backup,sizeof used);// 复制回来return res;}// IDA*booldfs(int depth){
if(depth +f()> max_depth)returnfalse;for(int i =0; i < idx; i ++)// 枚举所有的正方形if(check(square[i]))// 如果第 i 个正方形还没被破坏{
// 那么枚举该正方形的所有边编号,去掉该边并继续爆搜for(int j =0; j <(int)square[i].size(); j ++){
used[square[i][j]]=true;if(dfs(depth +1))returntrue;used[square[i][j]]=false;}returnfalse;// 如果每条边都爆搜不成功,那么说明删掉 max_depth 个火柴无法破坏该正方形}returntrue;// 如果所有的正方形都被破坏了,返回 true}intmain(){
int T;scanf("%d",&T);while(T --){
scanf("%d",&n), idx =0;// 初始化 idxmemset(used,false,sizeof used);// 初始化 usedfor(int len =1; len <= n; len ++)// 枚举 len, r, c,预处理每个正方形for(int r =0; r + len <= n; r ++)for(int c =0; c + len <= n; c ++)add(r, c, len);int k;scanf("%d",&k);while(k --)// 读入所有已经被破坏的边{
int x;scanf("%d",&x);used[x]=true;}max_depth =0;// IDA*while(!dfs(0)) max_depth ++;printf("%d\n", max_depth);}return0;}