题意
给一个 n * m 的图,每一个格子上写着 1 或 0。要求选出一个行的集合,由这个行集合构成的子图中,每一列有且只有一个 1。输出集合元素个数并输出集合,或输出NO。
思路
第一个DLX,学到了。
这类问题的学名叫精确覆盖问题。DLX准确地讲是一种数据结构,可以高效地进行矩阵的递归和回溯。
DLX详解:http://www.cnblogs.com/grenet/p/3145800.html
kuangbin的模板:http://www.cnblogs.com/kuangbin/p/3752854.html
题目链接
http://acm.hust.edu.cn/problem/show/1017
AC代码
可以说是默写了一遍 kuangbin 的模板
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn = 1010; //最大行数
const int maxm = 1010; //最大列数
const int maxv = maxn * 100; //最大节点数
//Dancing Links 类
struct DLX
{
int n, m, size; //行数、列数、节点数
int U[maxv], D[maxv], L[maxv], R[maxv], Row[maxv], Col[maxv]; //上、下、左、右指针,行、列号
int S[maxm], H[maxn]; //列的节点个数,行的头指针
int ansd, ans[maxn]; //答案栈
void init(int _n, int _m) //初始化函数
{
n = _n, m = _m;
for(int j= 0; j<= m; j++)
{
Row[j] = 0, Col[j] = j; //行、列号
U[j] = D[j] = j; //上下指针
S[j] = 0;
L[j] = j - 1, R[j] = j + 1; //左右指针
}
L[0] = m, R[m] = 0; //端点左右指针
size = m; //节点个数(初始 = 列数)(附加节点)
for(int i= 1; i<= n; i++) //行的头指针
H[i] = -1;
}
void link(int r, int c) //向链表中插入节点
{
size ++; //节点数目
Row[size] = r, Col[size] = c; //行号、列号
S[c] ++; //插入列链表
D[size] = D[c];
U[D[c]] = size;
D[c] = size;
U[size] = c;
if(H[r] == -1) 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;
}
}
void remove(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[Col[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])
{
U[D[j]] = j, D[U[j]] = j;
S[Col[j]] ++;
}
}
bool Dance(int d) //搜索函数
{
if(R[0] == 0) //剩余列为空,搜索成功
{
ansd = d;
return true;
}
int c = R[0]; //从节点少的列开始搜索,可以减少状态数
for(int j= R[0]; j!= 0; j= R[j])
if(S[j] < S[c]) c = j;
remove(c); //删除当前列
for(int i= D[c]; i!= c; i= D[i]) //遍历选取当前列中节点所在的行
{
ans[d] = Row[i]; //行号压入答案栈
for(int j= R[i]; j!= i; j= R[j]) remove(Col[j]); //删除行中节点对应的列
if(Dance(d+1)) return true; //向下一层搜索
for(int j= L[i]; j!= i; j= L[j]) resume(Col[j]); //搜索失败的话还原当前行
}
resume(c);
return false;
}
};
int n, m;
DLX g;
int main()
{
while(cin >> n >> m)
{
g.init(n, m); //初始化
for(int i= 1; i<= n; i++) //构造链表
{
int num;
cin >> num;
while(num --)
{
int j;
cin >> j;
g.link(i, j);
}
}
if(g.Dance(0)){ //DLX
printf("%d ", g.ansd);
for(int i= 0; i< g.ansd; i++)
printf("%d ", g.ans[i]);
printf("\n");
}
else printf("NO\n");
}
return 0;
}