這題可理解爲用最少用多少火柴棒覆蓋全部的正方形,以正方形爲列,火柴棒爲行,重複覆蓋模型明顯。
建圖的時候要找出所有正方形所包含的火柴棒,我是這樣找的:
先確定最左上邊的正方形(邊長爲1~n)所包含的邊,因爲對於等大的兩個正方形,其相同位置的火柴棒邊的標號的差是一定的,所以算出一個正方形的邊根據差就能得出其他的等大的正方形的邊了。
/*
第一道像樣一點的DLX重複覆蓋,這題數據弱~
*/
#include <cstdio>
#include <cstring>
#include <vector>
#include <iostream>
using namespace std;
const int NN=100;
const int MM=10000;
const int a[7]={0,1,2,6,15,31,56};
const int lim[6]={0,1,3,6,9,14};
vector<int> have[NN];
int n,ans,cnt,U[MM],D[MM],L[MM],R[MM],C[MM],H[MM],V[NN],S[NN],hash[NN];
bool destr[NN];
void init(int rows,int columns)
{
int i;
for (i=0; i<=columns; i++)
{
C[i]=U[i]=D[i]=i;
L[i+1]=i;
R[i]=i+1;
S[i]=0;
}
cnt=L[0]=columns; R[columns]=0;
for (i=1; i<=rows; i++) V[i]=0;
}
void link(int i,int j)
{
S[C[++cnt]=j]++;
H[cnt]=i;
D[cnt]=j;
U[cnt]=U[j];
if (V[i]) L[cnt]=V[i],R[cnt]=R[V[i]];
else L[cnt]=R[cnt]=cnt;
V[i]=cnt;
D[U[cnt]]=cnt;
U[D[cnt]]=cnt;
L[R[cnt]]=cnt;
R[L[cnt]]=cnt;
}
void build()
{
int i,j,k,o,l,t,size;
bool flag;
int N=n+n+1;
for (i=0; i<=a[n+1]-1; i++) have[i].clear();
for (t=1; t<=n; t++)
{
l=n-t+1;
k=a[t];
for (i=1; i<=l; i++) have[k].push_back(i); //up
for (j=1,i=n+1; j<=l; i+=N,j++) have[k].push_back(i); //left
for (j=1,i=n+1+l; j<=l; i+=N,j++) have[k].push_back(i); //right
for (j=1,i=N*l+1; j<=l; i++,j++) have[k].push_back(i); //down
for (i=0; i<t; i++)
for (j=0; j<t; j++)
{
if (i==0 && j==0) continue;
k++;
for (o=0; o<4*l; o++) have[k].push_back(have[a[t]][o]+i*N+j);
}
}
init(2*n*(n+1),a[n+1]-1);
for (i=1; i<=a[n+1]-1; i++)
{
size=have[i].size();
flag=true;
for (j=0; j<size && flag; j++) if (destr[have[i][j]]) flag=false;
if (!flag)
{
R[L[i]]=R[i];
L[R[i]]=L[i];
continue;
}
for (j=0; j<size; j++) link(have[i][j],i);
}
}
int h() //啓發函數
{
int i,j,k,ret=0;
memset(hash,0,sizeof(hash));
for (i=R[0]; i; i=R[i])
{
if (hash[C[i]]) continue;
ret++;
hash[C[i]]=1;
for (j=D[i]; j!=i; j=D[j])
for (k=R[j]; k!=j; k=R[k]) hash[C[k]]=1;
}
return ret;
}
inline void remove(int c)
{
for (int i=D[c]; i!=c; i=D[i])
{
L[R[i]]=L[i];
R[L[i]]=R[i];
}
}
inline void resume(int c)
{
for (int i=U[c]; i!=c; i=U[i])
{
R[L[i]]=i;
L[R[i]]=i;
}
}
void dance(int k)
{
if (k+h()>=ans) return;
if (!R[0])
{
if (k<ans) ans=k;
return;
}
int i,j,c,tmp=MM;
for (i=R[0]; i; i=R[i]) if (S[i]<tmp) tmp=S[c=i];
for (i=D[c]; i!=c; i=D[i])
{
remove(i);
for (j=R[i]; j!=i; j=R[j]) remove(j);
dance(k+1);
for (j=L[i]; j!=i; j=L[j]) resume(j);
resume(i);
}
}
int main()
{
int T,k,x;
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&n,&k);
if (k==0) { printf("%d\n",lim[n]); continue; }
memset(destr,0,sizeof(destr));
for (int i=1; i<=k; i++)
{
scanf("%d",&x);
destr[x]=1;
}
build();
ans=lim[n];
dance(0);
printf("%d\n",ans);
}
return 0;
}