题目大意:给出一个01矩阵,让你选出几行,使得所有的列有且只有1个1。输出选出的行的行数和行号。
解法:这是一种精确覆盖模型。用dancinglink,看了很多博客,看吐了终于理解了。不得不说dancinglink很神奇,不过写起来太麻烦。搞一个模版以后用就好了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define max1 100300
#define max2 1010
using namespace std;
int R[max1],L[max1],U[max1],D[max1];//x的右左上下。
int C[max1],mark[max1];//x所在的列号,x所在的行号。
int h[max2],s[max2];//左右头指针,x列1的个数。
int num,n,m,ans[max2],flag;
void link(int r,int c)//建图
{ //连接列
s[c]++;C[num] = c;
U[num] = U[c];D[U[c]] = num;
U[c] = num;D[num] = c;
//连接行
if(h[r]==-1) h[r] = L[num] = R[num] = num;
else
{
L[num] = L[h[r]];R[L[h[r]]] = num;
L[h[r]] = num; R[num] = h[r];
}
mark[num] = r;
num++;
}
void remove1(int c)
{
L[R[c]] = L[c];R[L[c]] = R[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 reuse(int c)
{
for(int i=U[c];i!=c;i=U[i])//列
{
for(int j=L[i];j!=i;j=L[j])//恢复行。
{
U[D[j]]=j;D[U[j]]=j;
s[C[j]]++;
}
}
L[R[c]]=c;
R[L[c]]=c;
}
void dancing(int k)
{
if(R[0]==0)//所有的列都被覆盖了。
{
flag = 1;
printf("%d",k);
for(int i = 0 ; i < k ; i ++)
{
printf(" %d",mark[ans[i]]);
}
printf("\n");
return ;
}
int min1=0x7fffffff;
int c = 0;
for(int i = R[0] ; i != 0 ; i = R[i])
{//选列数中1比较少的列来作为起始点。
if(min1>s[i])
{
min1 = s[i];
c = i;
}
}
remove1(c);
for(int i = D[c];i != c ; i = D[i])
{
ans[k] = i;//选行
for(int j = R[i] ; j != i ; j = R[j])
remove1(C[j]);//删除列对应的行
dancing(k+1);
if(flag) return;
for(int j = L[i] ; j !=i ; j = L[j])
reuse(C[j]);//恢复列对应的行
}
reuse(c);
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i = 0 ; i <= m ; i ++)
{
s[i] = 0;
U[i] = D[i] = i;
R[i] = i+1;
L[i+1] = i;
}
R[m] = 0;
num = m + 1;
memset(h,-1,sizeof(h));
memset(mark,0,sizeof(mark));
for(int i = 1 ; i <= n ; i++)
{
int t;
cin>>t;
while(t--)
{
int x;
cin>>x;
link(i,x);
}
}
flag = 0;
dancing(0);
if(!flag) printf("NO\n");
}
return 0;
}