关键是建好图
把n*m的矩阵看成n*m个单位元,作为n*m列;每一个矩形一行。
问题即转化为从这些行中选择最少的一部分使每一列被覆盖且仅覆盖一次。
#include<cstdio>
#define inf 10000000
#define N 1005
#define M 1024*505
int U[M],D[M],L[M],R[M],C[M];//C代表M所属的列,U,D,L,R为一个元素的上下左右指针
int H[505];//H是水平循环链表的头指针
int S[1005];//S代表每一列的元素个数
int size,n,m,p,ans;
void remove(int c)
{
R[L[c]]=R[c],L[R[c]]=L[c];
for(int i=D[c]; i!=c; i=D[i])
for(int j=R[i]; j!=i; j=R[j])
U[D[j]]=U[j],D[U[j]]=D[j],--S[C[j]];
}
void resume(int c)
{
R[L[c]]=L[R[c]]=c;
for(int i=U[c]; i!=c; i=U[i])
for(int j=L[i]; j!=i; j=L[j])
++S[C[U[D[j]]=D[U[j]]=j]];
}
void Dance(int k)
{
int i,j,tmp,c;
if(!R[0])
{
if(k<ans)
ans=k;
return;
}
for(tmp=inf,i=R[0]; i; i=R[i])
if(S[i]<tmp)tmp=S[c=i];
remove(c);
for(i=D[c]; i!=c; i=D[i])
{
for(j=R[i]; j!=i; j=R[j])remove(C[j]);
Dance(k+1);
for(j=L[i]; j!=i; j=L[j])resume(C[j]);//在这里遵循的原则是先删除的后还原,后删除的先还原。
}
resume(c);
}
void Link(int r,int c)
{
++S[C[size]=c];
D[size]=D[c];
U[D[c]]=size;
U[size]=c;
D[c]=size;
if(H[r]<0)H[r]=L[size]=R[size]=size;
else
{
R[size]=R[H[r]];
L[R[H[r]]]=size;
L[size]=H[r];
R[H[r]]=size;
}
size++;
}
int main()
{
int i,j,w,num,T,t;
scanf("%d",&T);
for(t=1;t<=T;t++)
{
ans=inf;
size=0;
scanf("%d %d %d",&n,&m,&p);
int tem=n*m;
for(i=0; i<=tem; ++i)
{
S[i]=0;
D[i]=U[i]=i;
L[i+1]=i;
R[i]=i+1;
}
R[tem]=0;
L[0]=tem;
size=tem+1;
for(i=1; i<=p; ++i)
{
H[i]=-1;
int a,b,c,d;
scanf("%d %d %d %d",&a,&b,&c,&d);
for(j=a;j<c;j++)
for(w=b+1;w<=d;w++)
Link(i,j*m+w);
}
Dance(0);
if(ans==inf)
printf("-1\n");
else
printf("%d\n",ans);
}
return 0;
}