|
/**//* 从昨天下午就开始搞,今天中午才搞定,Dancing Links 果然高深 神奇的双向环形十字链表······转的头晕了-_-||不过蛮有成就感的^v^ 记录一下解题过程,先反反复复看了一遍Donald E.Knuth的论文,就开始攻hust 1017这道题目, 题目意思很明确: 给定一个由0和1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1。
论文中有详细解答,具体思路就是: 1. 任意选择一列c,然后删除它(这个删除并不是普通的把这一列删除,而是要将这一列中有1的格子所在的行全部删除) 2. 然后对这一列上有1的每一行进行枚举,当枚举到第i行时 该行上所有有1的列j全部删除(同上删除法) 3. 然后递归进入下一次,直到所有的列均被删除则有解 4. 恢复对j的删除 5. 恢复对c的删除 */
#include <iostream>
using namespace std;
struct point { int L; int R; int U; int D; int Sum; int x, y; }p[ 1010 * 1010 ];
int n, m; int i, j, k; int map[1001][1001]; int sor[1001]; int flag; int stack[1001], top;
int Num(int x, int y) { return x * 1001 + y; } //删除c列 void CoverCol(int c) { int i, j;
p[ p[ c ].R ].L = p[ c ].L; p[ p[ c ].L ].R = p[ c ].R; //删除c列中每个有1的行 i = c; for(i = p[i].D; i != c; i = p[i].D) { j = i; p[ p[i].y ].Sum --; for(j = p[j].R; j != i; j = p[j].R) { p[ p[j].D ].U = p[ j ].U; p[ p[j].U ].D = p[ j ].D; } } } //恢复c列 void Release(int c) { int i, j;
p[ p[ c ].R ].L = c; p[ p[ c ].L ].R = c; //恢复c列中每个有1的行 i = c; for(i = p[i].U; i != c; i = p[i].U) { j = i; p[ p[i].y ].Sum ++; for(j = p[j].L; j != i; j = p[j].L) { p[ p[j].D ].U = j; p[ p[j].U ].D = j; } } }
int dfs(int k) { int i, j, l, m;
if(flag) return 1;
//得解输出 if(p[ 0 ].R == 0) { printf("%d", top); for(i = 0; i < top; i++) printf(" %d", stack[i]); puts(""); flag = 1; return 1; }
int c = 0; //每次取出没有被覆盖的并且1的个数最小的一列 int Min = INT_MAX; i = c;
for(i = p[i].R; i ; i = p[i].R) { if(p[ p[i].y ].Sum < Min) { Min = p[ p[i].y ].Sum; c = i; } }
//将这一列删除 CoverCol(c); i = c; //枚举c列中的每一行 for(i = p[i].D; i != c; i = p[i].D) { //p[i].x 作为当前枚举的行,进栈 stack[ top++ ] = p[i].x; j = i;
//对于该枚举的行,删除该行上1的格子所在的列 for(j = p[j].R; j != i; j = p[j].R) { CoverCol(p[j].y); } if ( dfs(k+1) ) return 1;
//对于该枚举的行,恢复该行上1的格子所在的列 j = i; for(j = p[j].L; j != i; j = p[j].L) { Release(p[j].y); } top --; } //恢复c Release(c); return 0; }
int main() {
int T = 0; while(scanf("%d %d", &n, &m) != EOF) { T ++;
for(i = 1; i <= n; i++) { scanf("%d", &k); for(j = 0; j < k; j++) { scanf("%d", &sor[j]); map[i][sor[j]] = T; }
int lef = Num(i, sor[0]); int rig = Num(i, sor[k-1]);
p[ lef ].L = rig; p[ lef ].x = i; p[ lef ].y = sor[0]; for(j = 1; j < k; j++) { int cur = Num(i, sor[j]); p[ Num(i, sor[j-1]) ].R = cur;
p[ cur ].L = Num(i, sor[j-1]); p[ cur ].x = i; p[ cur ].y = sor[j]; } p[ rig ].R = lef;
}
p[0].R = 1;
for(i = 1; i <= m; i++) { int No = Num(0, i); if(i + 1 <= m) p[ No ].R = Num(0, i+1); else p[ No ].R = 0;
p[ No ].L = Num(0, i-1); p[ No ].x = 0; p[ No ].y = i; p[ No ].Sum = 0;
int last = No;
for(j = 1; j <= n; j++) { if( map[j][i] == T ) { p[ last ].Sum ++; int now = Num(j, i);
p[ No ].D = now; p[ now ].U = No; No = now; } } p[ No ].D = Num(0, i); p[ Num(0, i) ].U = No;
if( !p[ last ].Sum ) { printf("NO\n"); break; } }
if(i == m + 1) { flag = 0; top = 0; dfs(0); if(!flag) puts("NO"); } } }
|