题目链接:http://acm.hust.edu.cn/JudgeOnline/problem.php?id=1017
题意:给定一个01矩阵,要求选择一些行,使每一列有且仅有一个1.
Dancing Links论文中讲到的一题,并以此为基础使另外一些类型的题目转化为此种精确区间覆盖模型用DLX解决。
算法描述如下:
深搜:
1、如果矩阵为空,得到结果,返回
2、从矩阵中选择一列,以选取最少元素的列为优化方式
3、删除该列及其覆盖的行
4、对该列的每一行元素:
删除一行及其覆盖的列,
5、进行下一层搜索,如果成功则返回
6、恢复现场,跳至4
7、恢复所选择行
用双向十字链表来维护该矩阵,方便删除与恢复,其中删除操作:
R[L[i]]=L[i];
L[R[i]]=R[i];
恢复操作:
R[L[i]]=i;
L[R[i]]=i;
代码:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
const int head=0;
const int INF=0x7ffffff;
const int N=1008;
const int M=N*N;
int U[M],D[M],L[M],R[M];
int C[M],row[M],S[N],O[N];
int ak;
int n,m;
void Remove(int c)
{//删除一列,并删除同列覆盖的每行
int i,j;
L[R[c]]=L[c];
R[L[c]]=R[c];
for(i=D[c];i!=c;i=D[i]){
for(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)
{//恢复一列及此列覆盖的行
int i,j;
for(i=U[c];i!=c;i=U[i]){
for(j=L[i];j!=i;j=L[j]){
U[D[j]]=D[U[j]]=j;
S[C[j]]++;
}
}R[L[c]]=L[R[c]]=c;
}
int Dfs(int k)
{
int min,c,i,j;
if(R[head]==head){//得到结果
ak=k;return 1;
}
for(min=INF,c=0,i=R[head];i!=head;i=R[i]){
if(S[i]<min) min=S[i],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]);//删除
O[k]=row[i];//记录结果
if(Dfs(k+1))
return 1;
for(j=L[i];j!=i;j=L[j])
Resume(C[j]);//恢复
}
Resume(c);
return 0;
}
int main()
{
int i,j;
int num, x,size,rowh;
while(~scanf("%d%d", &n, &m)){
memset(S, 0, sizeof(S));
for(i=1;i<=m;i++)//初始化列头节点
R[i-1]=L[i+1]=U[i]=D[i]=i;
R[head]=1;R[m]=0;
size=m+1;
for(i=0; i<n; i++){
rowh=-1;//行头节点
scanf("%d", &num);
for(j=0; j<num; j++){
scanf("%d", &x);
//所在列
C[size]=x;
//插入列
U[size]=U[x];
D[U[x]]=size;
U[x]=size;
D[size]=x;
row[size]=i+1;
S[x]++;
//插入行
if(rowh==-1){
L[size]=R[size]=size;
rowh=size;
}
else{
L[size]=L[rowh];
R[L[rowh]]=size;
L[rowh]=size;
R[size]=rowh;
}size++;
}
}
if(!Dfs(0))puts("NO");
else{
printf("%d",ak);
for(i=0;i<ak;i++)
printf(" %d",O[i]);
puts("");
}
}
return 0;
}