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;
}