1017 - Exact cover
Problem's Link: http://acm.hust.edu.cn/problem/show/1017
Mean:
给定一个由0-1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1
analyse:
初学DLX。
这是DLX处理的最简单的问题,也是模板题。
Time complexity: O(n*d)
Source code:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <vector> #include <queue> #include <set> #include <map> #include <string> #include <math.h> #include <stdlib.h> #include <time.h> using namespace std; const int MAXNode = 100010; const int MAXN = 1010; struct DLX { int n,m,size; int U[MAXNode],D[MAXNode],R[MAXNode],L[MAXNode],Row[MAXNode],Col[MAXNode]; int H[MAXN], S[MAXN]; // H[i]---第i行第一个为1的index S[i]---第i列为1的个数 int ansd, ans[MAXN]; void init(int _n,int _m) { n = _n; m = _m; for(int i = 0;i <= m;i++) // 初始化第一行(图中的C[]) { S[i] = 0; // 第i列为1的个数 U[i] = D[i] = i; L[i] = i-1; R[i] = i+1; } R[m] = 0; L[0] = m; // 第一行的最后一个指向第一行的第一个(成环) size = m; // 从m开始以后的都是普通结点 for(int i = 1;i <= n;i++) H[i] = -1; // H[i]---第i行第一个为1的结点编号 } void Link(int r,int c) // 行 列 { // D[c] --- 第c列的下指针 S[Col[++size]=c]++; // 普通结点下标++ 第size个结点的列数是c 第c列的结点个数++ Row[size] = r; // 第size个结点的行数是r D[size] = D[c]; // 第size个结点的下指针是:第0行第c列的下指针 U[size] = c; // 第size个结点的上指针是:第0行第c列 (只有输入行是递增时才可以这样) U[D[c]] = size; // 第0行第c列的上指针是:size D[c] = size; // size上面那个的下指针是: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; } } void remove(int c) // 删除列c及其所在的行 { L[R[c]] = L[c]; R[L[c]] = R[c]; // 左右两个结点连接,屏蔽掉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]]; // j所在的列的数目减少 } } void resume(int c) //恢复列c缩对应的行 { for(int i = U[c];i != c;i = U[i]) for(int j = L[i];j != i;j = L[j]) ++S[Col[U[D[j]]=D[U[j]]=j]]; L[R[c]] = R[L[c]] = c; } //d为递归深度 bool Dance(int d) { if(R[0] == 0) // R[0]==R[m] // 第0行已经没有结点 { ansd = d; return true; } int c = R[0]; for(int i = R[0];i != 0;i = R[i]) // 往右走 ( 找出结点数最少的一列) if(S[i] < S[c]) //第i列结点个数 < 第c列结点个数 c = i; remove(c); // 移除列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; } }; DLX g; int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int n,m; while(scanf("%d%d",&n,&m) == 2) { g.init(n,m); for(int i = 1;i <= n;i++) // 行 { int num,j; scanf("%d",&num); while(num--) { scanf("%d",&j); // 列 g.Link(i,j); } } if(!g.Dance(0)) printf("NO\n"); else { printf("%d",g.ansd); for(int i = 0;i < g.ansd;i++) printf(" %d",g.ans[i]); printf("\n"); } } return 0; }
这个博客讲得非常细:
http://www.cnblogs.com/grenet/p/3145800.html