一些搜索题

1,poj-2308

http://poj.org/problem?id=2308

很有意思的题,挺少人写

连连看的小游戏(只有4种牌),判断是否能全部消除。思路非常简单但是要注意减枝。 
外层的DFS指定一张牌来想办法消除它,内层的BFS以指定的这张牌为起点,向四周扩展找到能和它配对的牌。然后每一对能消除的牌都考虑一遍。 
1.连的线不能越界到格子外边; 
2.有的牌只有奇数个,无解 
3.出现了 A B 这种情况而且,AB分别只有两张,那么肯定无解;不加这个减枝会超时。 
。。。。B A
code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
using namespace std;
#define N 12
int n, m, cnt, len, su;
struct p {
	int x, y, di, ti;
}a[10*N];
int mp[N][N], res[5], vis[N][N], cer[10*N][2];
int dir[4][2] = { 1,0,-1,0,0,1,0,-1 };
void bfs(p h,int w) {
	memset(vis, 0, sizeof(vis));
	len = 0;
	h.di = -1;h.ti = 0;
	queue<p>q;
	q.push(h);
	vis[h.x][h.y] = 1;
	while (!q.empty()) {
		p c = q.front();
		q.pop();
		if (mp[c.x][c.y] == w) {
			cer[len][0] = c.x;
			cer[len++][1] = c.y;
			continue;
		}
		for (int i = 0; i < 4; i++) {
			int xx = c.x + dir[i][0];
			int yy = c.y + dir[i][1];
			if (xx >= 0 && xx < n&&yy >= 0 && yy < m&&!vis[xx][yy]) {
				if (mp[xx][yy] != w && mp[xx][yy] != -1)continue;
				p cc;
				if (c.di == i || c.di == -1)
					cc.ti = c.ti;
				else
					cc.ti = c.ti + 1;
				if (cc.ti > 2)continue;
				cc.x = xx, cc.y = yy, cc.di = i;
				q.push(cc);
				vis[xx][yy] = 1;
			}
		}
	}
}
bool check()
{
	for (int i = 0; i < n-1; i++)
		for (int j = 0; j < m-1; j++)
		{
			if (mp[i][j] != -1 && mp[i][j + 1] != -1 && mp[i][j] != mp[i][j + 1])
			{
				if (mp[i][j] == mp[i + 1][j + 1] && mp[i][j + 1] == mp[i + 1][j] && res[mp[i][j]] == 2 && res[mp[i][j + 1]] == 2)
					return false;
			}
		}
	return true;
}
int flag = 1;
void dfs(int sum) {
	if (!flag)return;
	if (sum == 0) {
		flag = 0;
		return;
	}
	if (!check())return;
	for (int i = 0; i < su; i++) {
		if (!flag)return;
		if (mp[a[i].x][a[i].y] != -1) {
			int w = mp[a[i].x][a[i].y];
			mp[a[i].x][a[i].y] = -1;
			bfs(a[i],w);
			for (int j = 0; j < len; j++) {
				mp[cer[j][0]][cer[j][1]] = -1;
				res[w] -= 2;
				dfs(sum - 2);
				res[w] += 2;
				mp[cer[j][0]][cer[j][1]] = w;
			}
			mp[a[i].x][a[i].y] = w;
		}
	}
}
int main() {
	char temp;
	while (~scanf("%d%d", &n, &m)) {
		if (n == 0 && n == m)break;
		memset(res, 0, sizeof(res));
		cnt = 0;
		cin.get();
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				scanf("%c", &temp);
				if (temp != '*') {
					mp[i][j] = temp - 'A';
					res[temp - 'A']++;
					a[cnt].x = i;
					a[cnt++].y = j;
				}
				else 
					mp[i][j] = -1;
			}
			cin.get();
		}
		int sum = res[0] + res[1] + res[2] + res[3];
		if (res[0] % 2 || res[1] % 2 || res[2] % 2 || res[3] % 2) {
			cout << "no" << endl;
			continue;
		}
		su = sum;
		dfs(sum);
		if (flag)cout << "no" << endl;
		else cout << "yes" << endl;
	}
	return 0;
}

2,poj-1190

剪枝,下面是一个大佬的代码,2*(n-sumv)/r+sums>=best这个剪枝比较难想:因为体积是固定的,在相同体积下只有一个圆柱的情况面积最小,此时如果还大于已找到的当前最小面积就直接跳出。

code:

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=101;
const int inf=0x3f3f3f3f;
int n,m,minv[N],mins[N],best; 
void init(){
    minv[0]=0;mins[0]=0;
    for(int i=1;i<22;i++){//从顶层向下计算出最小体积和表面积的可能值
        minv[i]=minv[i-1]+i*i*i;
        mins[i]=mins[i-1]+2*i*i;
    }//从顶层(即第1层)到第i层的最小体积minv[i]成立时第j层的半径和高度都为j
}
//dep:搜索深度,从底层m层向上搜,r,h分别为该层的半径和高度
void dfs(int dep,int sumv,int sums,int r,int h){
    if(!dep){//搜索完成,则更新最小面积值
        if(sumv==n&&sums<best) best=sums;
        return ;
    }
    //剪枝自行脑补 
    if(sumv+minv[dep-1]>n||sums+mins[dep]>best||2*(n-sumv)/r+sums>=best)return ;
    for(int i=r-1;i>=dep;i--){//按递减顺序枚举dep层蛋糕半径的每一个可能值,这里第dep层的半径最小值为dep
        if(dep==m) sums=i*i;//底面积作为外表面积的初始值(总的上表面积,以后只需计算侧面积)
        //最大高度,即dep层蛋糕高度的上限,(n-sumv-minv[dep-1])表示第dep层最大的体积
        int maxh=min((n-sumv-minv[dep-1])/(i*i),h-1);
        for(int j=maxh;j>=dep;j--){//同理,第dep层的最小高度值为dep
            dfs(dep-1,sumv+i*i*j,sums+2*i*j,i,j);//递归搜索子状态
        }
    }
}
int main(){
    init();
    while(scanf("%d%d",&n,&m)==2){
        best=inf;
        dfs(m,0,0,n+1,n+1);
        printf("%d\n",best==inf?0:best); 
    }
    return 0;
}

3,poj-1376

裸bfs,但需要注意细节

code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
using namespace std;
int n, m, xs, ys, xe, ye;
int mp[55][55], vis[55][55][4];
int dir[4][2] = { 1,0,0,1,-1,0,0,-1 };
struct node {
	int x, y, d;
	int k;
	bool operator<(node n)const {
		return k > n.k;
	}
}h, t;
void bfs() {
	int flag = 0;
	node r;
	priority_queue<node>q;
	q.push(h);
	while (!q.empty()) {
		t = q.top();
		q.pop();
		if (t.x == xe && t.y == ye) {
			flag = 1;
			cout << t.k << endl;
			break;
		}
		if (t.d == 0 || t.d == 2) {
			r = t; r.k++;
			if (!vis[t.x][t.y][1]) {
				r.d = 1,vis[r.x][r.y][1] = 1;
				q.push(r);
			}
			if (!vis[t.x][t.y][3]) {
				r.d = 3,vis[r.x][r.y][3] = 1;
				q.push(r);
			}
		}
		else {
			r = t; r.k++;
			if (!vis[t.x][t.y][0]) {
				r.d = 0;
				vis[r.x][r.y][0] = 1;
				q.push(r);
			}
			if (!vis[t.x][t.y][2]) {
				r.d = 2;
				vis[r.x][r.y][2] = 1;
				q.push(r);
			}
		}
		for (int i = 1; i <= 3; i++) {
			r.x = t.x + i * dir[t.d][0];
			r.y = t.y + i * dir[t.d][1];
			if (mp[r.x][r.y])break;
			if (r.x >= 1 && r.x <= n - 1 && r.y >= 1 && r.y <= m - 1 && !vis[r.x][r.y][t.d]) {
				r.d = t.d;
				r.k = t.k + 1;
				q.push(r);
				vis[r.x][r.y][r.d] = 1;
			}
		}
	}
	if (!flag)cout << "-1" << endl;
}
int main() {
	char str[10];
	int t;
	while (1) {
		scanf("%d%d", &n, &m);
		if (n == 0 && n == m)break;
		memset(mp, 0, sizeof(mp));
		memset(vis, 0, sizeof(vis));
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				scanf("%d", &t);
				if (t == 1) {
					mp[i][j] = 1;
					if (i - 1 >= 1)mp[i - 1][j] = 1;
					if (j - 1 >= 1)mp[i][j - 1] = 1;
					if (i - 1 >= 1 && j - 1 >= 1)mp[i - 1][j - 1] = 1;
				}
			}
		}
		scanf("%d%d%d%d%s", &xs, &ys, &xe, &ye, &str);
		h.x = xs;
		h.y = ys;
		h.k = 0;
		if (str[0] == 's') {
			vis[xs][ys][0] = 1;
			h.d = 0;
		}
		if (str[0] == 'e') {
			vis[xs][ys][1] = 1;
			h.d = 1;
		}
		if (str[0] == 'n') {
			vis[xs][ys][2] = 1;
			h.d = 2;
		}
		if (str[0] == 'w') {
			vis[xs][ys][3] = 1;
			h.d = 3;
		}
		bfs();
	}
	return 0;
}

4,poj-2688

http://poj.org/problem?id=2688

题意:给一个n*m的矩阵,机器人的位于某点,问机器人把所以垃圾清理的最短路。

先用bfs求出机器人到每一个垃圾的最短距离,与每个垃圾到其他垃圾的距离,建好图,然后就转换成了tsp问题(这里用dp解决)

code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<vector>
#include<set>
using namespace std;
#define inf 0x3f3f3f
int n, m, cnt;
int res = inf;
int mp[31][31], vis[31][31], dis[35][35], dp[12][1 << 12];
int dir[4][2] = { 1,0,0,1,-1,0,0,-1 };
struct node {
	int x, y, k;
}st, a[32];
void init() {
	cnt = 0; res = inf;
	memset(vis, 0, sizeof(vis));
	memset(dis, -1, sizeof(dis));
}
bool bfs() {
	int t = 0;
	queue<node>q;
	q.push(st);
	vis[st.x][st.y] = 1;
	while (!q.empty()) {
		node h = q.front();
		q.pop();
		if (mp[h.x][h.y]>0) {
			t++;
			dis[0][mp[h.x][h.y]] = h.k;
			dis[mp[h.x][h.y]][0] = h.k;
			if (t == cnt) { return true; }
		}
		for (int i = 0; i < 4; i++) {
			st.x = h.x + dir[i][0];
			st.y = h.y + dir[i][1];
			if (st.x <= m && st.x > 0 && st.y <= n && st.y > 0 && !vis[st.x][st.y] && mp[st.x][st.y] != -1) {
				st.k = h.k + 1;
				q.push(st);
				vis[st.x][st.y] = 1;
			}
		}
	}
	return false;
}
void bfs1(int u) {
	int t = 0;
	queue<node>q;
	q.push(a[u]);
	vis[a[u].x][a[u].y] = 1;
	mp[a[u].x][a[u].y] = 0;
	while (!q.empty()) {
		node h = q.front();
		q.pop();
		if (mp[h.x][h.y] > 0) {
			dis[u][mp[h.x][h.y]] = h.k;
			dis[mp[h.x][h.y]][u] = h.k;
			t++;
			if (t == cnt) { break; }
		}
		for (int i = 0; i < 4; i++) {
			st.x = h.x + dir[i][0];
			st.y = h.y + dir[i][1];
			if (st.x <= m && st.x > 0 && st.y <= n && st.y > 0 && !vis[st.x][st.y] && mp[st.x][st.y] != -1) {
				st.k = h.k + 1;
				q.push(st);
				vis[st.x][st.y] = 1;
			}
		}
	}
}
void solve() {
	int s = (1 << cnt);
	for (int i = 1; i <= cnt; i++) {
		dp[i][0] = 0;
	}
	dp[0][s - 1] = inf;
	for (int i = 1; i < s - 1; i++) {
		for (int j = 1; j <= cnt; j++) {
			if ((i&(1 << (j - 1))) == 0) {
				int m=inf;
				for (int k = 1; k <= cnt; k++) {
					if (((1 << (k - 1)) & i) > 0) {
						m=min(dp[k][i-(1<<(k-1))]+dis[j][k],m);
					}
				}
				dp[j][i]=m;
			}
		}
	}
	for (int i = 1; i <= cnt; i++) {
		dp[0][s - 1] = min(dp[0][s - 1], dis[0][i] + dp[i][(s - 1) - (1 << (i-1))]);
	}
	cout << dp[0][s - 1] << endl;
}
int main() {
	char temp;
	while (1) {
		scanf("%d%d", &n, &m);
		if (n == 0 && n == m)break;
		init();
		cin.get();
		for (int i = 1; i <= m; i++) {
			for (int j = 1; j <= n; j++) {
				scanf("%c", &temp);
				if (temp == '.')mp[i][j] = 0;
				else if (temp == 'x')mp[i][j] = -1;
				else if (temp == '*') { cnt++; mp[i][j] = cnt; a[cnt].x = i, a[cnt].y = j; }
				else { st.x = i, st.y = j, st.k = 0; mp[i][j] = 0; }
			}
			cin.get();
		}
		if (bfs()) {
			for (int i = 1; i <= cnt; i++) {
				memset(vis, 0, sizeof(vis));
				bfs1(i);
			}
			memset(dp, -1, sizeof(dp));
			solve();
		}
		else {
			cout << "-1" << endl;
		}
	}
	return 0;
}

5,poj-1635

题目大意:给出两串含有‘1’和‘0’的字符串,0表示向下搜索,1表示回溯,这样深搜一颗树
深搜完之后问这两棵树是不是同一棵树,因为树的结点顺序不同,所以可以导致树深搜的序列也不同

解题思路:其实画下样例答案显然易见,因为如果树是相同的,那么每个结点的子节点个数必然相同,
子节点可以看做成是某个结点可以深搜到的结点
我们只需要将所有的结点的子节点个数,然后【排序】之后比较每个结点的个数是否相同。因为排序之后
就是按子节点大小排序了,这样比较就是一个确定的顺序了
然后我们分析下字符串,树的结点数=‘0’的个数+根节点,我们计算子节点数可以将字符串里面的都求出来,
如果哪个下标是1,就将子节点数赋值为0即可,其实不影响什么

关于计算每个结点的子节点数,可以定义一个父亲数组,然后对每个结点都往根节点搜索,那么父亲结点的
子节点就+1,这样循环一边全部的结点就可以得出答案
code:

#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#include<vector>
#include<cstdio>
#include<string>
using namespace std;
int t;
string str1, str2;
string dfs(string a,int l,int r) {
	int cnt = 0; vector<string>s;
	int p = l;
	for (int i = l; i <= r; i++) {
		if (a[i] == '0')cnt++;
		else cnt--;
		if (cnt == 0) {
			s.push_back(dfs(a, p + 1, i - 1));
			p = i + 1;
		}
	}
	string res = "0";
	sort(s.begin(), s.end());
	for (int i = 0; i < s.size(); i++) {
		res += s[i];
	}
	res += "1";
	//cout << res << endl;
	return res;
}
int main() {
	scanf("%d", &t);
	while (t--) {
		cin >> str1 >> str2;
		str1 = dfs(str1, 0, str1.size()-1);
		str2 = dfs(str2, 0, str2.size()-1);
		if (str1 == str2)cout << "same" << endl;
		else cout << "different" << endl;
	}
	return 0;
}

6,poj-3322

http://poj.org/problem?id=3322

题意:
      讲的是一个游戏,就是在一个平面上滚动一个1*1*2的长方体的游戏,在本题里面的游戏规则是这样的:
(1)
     一开始给你箱子的状态,可能是横着也可能是竖着。
(2)
     每一次可以滚动箱子,但是每次滚动到的位置(1个或者2个)都必须不能是空的。
(3)
     有的位置只能经得起箱子一半的重量,有的能经得起真个箱子的重量,经得起一半的格子意思就是不能在当前的这个格子上吧箱子竖起来。
(4)然后问从起始状态到掉到终点给的洞里面的最小步数,掉到洞里面的时候必须是竖直掉下去的。

bfs暴搜,需要保存状态,有3种状态。

code;

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
using namespace std;
#define N 505
struct node {
	int x, y;
	int x1, y1;
	int k, ans;
}re, e[2];
int n, m, cnt = 0;
int mp[N][N], vis[N][N][3];
void init() {
	cnt = 0;
	memset(vis, 0, sizeof(vis));
}
void bfs() {
	int flag = 0;
	node st, ct;
	queue<node>q;
	st.ans = 0;
	if (cnt == 2) {
		st.x = e[0].x; st.x1 = e[1].x;
		st.y = e[0].y; st.y1 = e[1].y;
		if (st.x == st.x1) 
			st.k = 1;
		else
			st.k = 2;
	}
	else {
		st.x = e[0].x; st.y = e[0].y;
		st.k = 0;
	}
	vis[st.x][st.y][st.k] = 1;
	q.push(st);
	while (!q.empty()) {
		ct = q.front();
		//cout << ct.x << " " << ct.y << " " << ct.k<<" "<<ct.ans << endl;
		q.pop();
		if (ct.k == 0 && ct.x == re.x&&ct.y == re.y) {
			flag = 1;
			cout << ct.ans << endl;
			break;
		}
		if (!ct.k) {
			st.x = ct.x - 2; st.x1 = ct.x - 1;
			st.y = ct.y; st.y1 = ct.y; st.k = 2;
			if (st.x >= 0 && !vis[st.x][st.y][2] && ((mp[st.x][st.y] + mp[st.x1][st.y1]) >= 0)) {
				vis[st.x][st.y][2] = 1;
				st.ans = ct.ans + 1;
				q.push(st);
			}
			st.x = ct.x; st.x1 = ct.x;
			st.y = ct.y-2; st.y1 = ct.y-1; st.k = 1;
			if (st.y >= 0 && !vis[st.x][st.y][1] && ((mp[st.x][st.y] + mp[st.x1][st.y1]) >= 0)) {
				vis[st.x][st.y][1] = 1;
				st.ans = ct.ans + 1;
				q.push(st);
			}
			st.x = ct.x + 1; st.x1 = ct.x + 2;
			st.y = ct.y; st.y1 = ct.y; st.k = 2;
			if (st.x1 < n && !vis[st.x][st.y][2] && ((mp[st.x][st.y] + mp[st.x1][st.y1]) >= 0)) {
				vis[st.x][st.y][2] = 1;
				st.ans = ct.ans + 1;
				q.push(st);
			}
			st.x = ct.x; st.x1 = ct.x;
			st.y = ct.y + 1; st.y1 = ct.y + 2; st.k = 1;
			if (st.y1 < m && !vis[st.x][st.y][1] && ((mp[st.x][st.y] + mp[st.x1][st.y1]) >= 0)) {
				vis[st.x][st.y][1] = 1;
				st.ans = ct.ans + 1;
				q.push(st);
			}
		}
		else {
			if (ct.k == 1) {
				st.x = ct.x + 1; st.x1 = ct.x1 + 1;st.y = ct.y; st.y1 = ct.y1; st.k = 1;
				if (st.x < n && !vis[st.x][st.y][1] && ((mp[st.x][st.y] + mp[st.x1][st.y1]) >= 0)) {
					vis[st.x][st.y][1] = 1;
					st.ans = ct.ans + 1;
					q.push(st);
				}
				st.x = ct.x - 1; st.x1 = ct.x1 - 1;st.y = ct.y; st.y1 = ct.y1; st.k = 1;
				if (st.x >= 0 && !vis[st.x][st.y][1] && ((mp[st.x][st.y] + mp[st.x1][st.y1]) >= 0)) {
					vis[st.x][st.y][1] = 1;
					st.ans = ct.ans + 1;
					q.push(st);
				}
				st.x = ct.x; st.y = ct.y - 1; st.k = 0;
				if (st.y >= 0 && !vis[st.x][st.y][0] && mp[st.x][st.y]==1 ) {
					vis[st.x][st.y][0] = 1;
					st.ans = ct.ans + 1;
					q.push(st);
				}
				st.x = ct.x; st.y = ct.y + 2; st.k = 0;
				if (st.y < m && !vis[st.x][st.y][0] && mp[st.x][st.y]==1 ) {
					vis[st.x][st.y][0] = 1;
					st.ans = ct.ans + 1;
					q.push(st);
				}
			}
			else {
				st.x = ct.x; st.y = ct.y - 1; st.x1 = ct.x1; st.y1 = ct.y1 - 1; st.k = 2;
				if (st.y >= 0 && !vis[st.x][st.y][2] && ((mp[st.x][st.y] + mp[st.x1][st.y1]) >= 0)) {
					vis[st.x][st.y][2] = 1;
					st.ans = ct.ans + 1;
					q.push(st);
				}
				st.x = ct.x; st.y = ct.y + 1; st.x1 = ct.x1; st.y1 = ct.y1 + 1; st.k = 2;
				//cout << st.x << " " << st.y << " " << st.k<<" "<<st.x1<<" "<<st.y1 << "SAS"<<endl;
				if (st.y < m && !vis[st.x][st.y][2] && (mp[st.x][st.y] + mp[st.x1][st.y1]) >= 0) {
					//cout << st.x << " " << st.y <<" "<<st.k<< endl;
					vis[st.x][st.y][2] = 1;
					st.ans = ct.ans + 1;
					q.push(st);
				}
				st.x = ct.x + 2; st.y = ct.y; st.k = 0;
				if (st.x < n && !vis[st.x][st.y][0] && mp[st.x][st.y] == 1) {
					vis[st.x][st.y][0] = 1;
					st.ans = ct.ans + 1;
					q.push(st);
				}
				st.x = ct.x - 1; st.y = ct.y; st.k = 0;
				if (st.x >= 0 && !vis[st.x][st.y][0] && mp[st.x][st.y] == 1) {
					vis[st.x][st.y][0] = 1;
					st.ans = ct.ans + 1;
					q.push(st);
				}
			}
		}
	}
	if (!flag) {
		cout << "Impossible" << endl;
	}
}
int main() {
	char temp;
	while (~scanf("%d%d", &n, &m)) {
		if (n == 0 && n == m) break;
		init();
		cin.get();
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				scanf("%c", &temp);
				if (temp == '#') 
					mp[i][j] = -2;
				if (temp == '.')
					mp[i][j] = 1;
				if (temp == 'X') {
					mp[i][j] = 1;
					e[cnt].x = i;
					e[cnt++].y = j;
				}
				if (temp == 'E') {
					mp[i][j] = 0;
				}
				if (temp == 'O') {
					mp[i][j] = 1;
					re.x = i, re.y = j;
				}
			}
			cin.get();
		}
		bfs();
	}
	return 0
}

7,poj-1084

http://poj.org/problem?id=1084

题目大意:给一个n*n的用单位长度的木棍拼起来的网格图,给每个木棍按图示编号,编号范围1~2*n*(n+1).现在已知图中已经去掉了k个木棍,求还要至少去掉几根木棍能使网格图中不存在正方形.即破坏图中所有的正方形.n不超过5.

题目分析:n太小了啦,直接爆搜!dancing links优化之.将之转化成一个重复覆蓋的模型.

n*n的完全网格图中存在n*(n+1)*(2*n+1)/6个正方形.给每一个编号,然后小木棍的编号图中给了,就按那个来.然后小木棍为行,正方形为列,建图.

注意题目已经给定的k个木棍要去掉.首先那k个木棍覆蓋的正方形是不用建图的,然后那k个表头也要删掉.
code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
using namespace std;
#define N 10005
int L[N], R[N], U[N], D[N], H[N], vis[100], Col[N], sel[N];
int S[100];
int Size;
int t, n, m, ans;
vector<int>mp[100];
void init(int k) {
	for (int i = 0; i <= k; i++) {
		S[i] = 0;
		Col[i] = i;
		U[i] = D[i] = i;
		R[i] = i + 1;
		L[i] = i - 1;
	}
	R[k] = 0; L[0] = k;
	Size = k;
	for (int i = 0; i <= 2 * n * (n + 1); i++)
		H[i] = -1;
}
void link(int r, int c) {
	Col[++Size] = c;
	D[Size] = D[c]; U[D[c]] = Size;
	U[Size] = c; D[c] = Size;
	if (H[r] < 0)H[r] = R[Size] = L[Size] = Size;
	else {
		R[Size] = R[H[r]];
		L[R[H[r]]] = Size;
		L[Size] = H[r];
		R[H[r]] = Size;
	}
}
void build() {
	int  ts = 0, k = 1, nu = 0;
	for (int i = 0; i <= 100; i++) {
		mp[i].clear();
	}
	while (1) {
		if ((n - ts) == 0)break;
		k = 1;
		for (int i = 1; i <= (n - ts)*(n - ts); i++) {
			for (int j = 0; j <= ts; j++) {
				mp[i+nu].push_back(k + j);
				mp[i+nu].push_back(k + n + (2 * n + 1)*j);
				mp[i+nu].push_back(k + ts + n + 1 + (2 * n+1)*j);
				mp[i+nu].push_back(k + j + (ts + 1)*(2 * n + 1));
			}
			if (i % (n - ts) == 0) {
				k = k + (n + 2);
			}
			else
				k++;
		}
		nu = nu + (n - ts)*(n - ts);
		ts++;
	}
	init(nu);
	bool flag;
	for (int i = 1; i <= nu; i++) {
		flag = true;
		for (int j = 0; j < mp[i].size()&&flag; j++) {
			if (sel[mp[i][j]])flag = false;
		}
		if (!flag) {
			L[R[i]] = L[i]; R[L[i]] = R[i];
			continue;
		}
		for (int j = 0; j < mp[i].size(); j++)
			link(mp[i][j], i);
	}
}
void Remove(int c) {
	for (int i = D[c]; i != c; i = D[i]) {
		L[R[i]] = L[i]; R[L[i]] = R[i];
	}
}
void Resume(int c) {
	for (int i = U[c]; i != c; i = U[i]) {
		L[R[i]] = i; R[L[i]] = i;
	}
}
int h() {
	memset(vis, 0, sizeof(vis));
	int res = 0;
	for (int i = R[0]; i != 0; i = R[i]) {
		if (vis[Col[i]])continue;
		res++;
		vis[Col[i]] = true;
		for (int j = D[i]; j != i; j = D[j])
			for (int k = R[j]; k != j; k = R[k])
				vis[Col[k]] = true;
	}
	return res;
}
void dance(int d) {
	if (R[0] == 0) {
		ans = d;
		return;
	}
	if (d + h() > ans)return;
	int c = R[0];
	for (int i = R[0]; i != 0; i = R[i]) {
		if (S[i] < S[c])
			c = i;
	}
	for (int i = D[c]; i != c; i = D[i]) {
		Remove(i);
		for (int j = R[i]; j != i; j = R[j])Remove(j);
		dance(d + 1);
		for (int j = L[i]; j != i; j = L[j])Resume(j);
		Resume(i);
	}
}
int main() {
	int k;
	scanf("%d", &t);
	while (t--) {
		ans = 100000;
		memset(sel, 0, sizeof(sel));
		scanf("%d", &n);
		scanf("%d", &m);
		for (int i = 0; i < m; i++) {
			scanf("%d", &k);
			sel[k] = 1;
		}
		build();
		dance(0);
		cout << ans << endl;
	}
	//while (1);
	return 0;
}

 

点赞