Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 18790 | Accepted: 5184 |
Description
Input
Output
Sample Input
3 3 0 1 0 0 0 1 1 0 0 4 4 0 0 0 1 1 0 0 0 1 1 0 1 0 1 0 0
Sample Output
Yes, I found it It is impossible
Source
Dancing Links用的数据结构是交叉十字循环双向链
而Dancing Links中的每个元素不仅是横向循环双向链中的一份子,又是纵向循环双向链的一份子。
因为精确覆盖问题的矩阵往往是稀疏矩阵(矩阵中,0的个数多于1),Dancing Links仅仅记录矩阵中值是1的元素。
对我们所举的例子,矩阵M的初始状态为
1 2 3 4 5 6 7
A 1 0 0 1 0 0 1
B 1 0 0 1 0 0 0
C 0 0 0 1 1 0 1
D 0 0 1 0 1 1 0
E 0 1 1 0 0 1 1
F 0 1 0 0 0 0 1
1、找出含1个数最少的列,即找出在子集中出现次数最少的那个元素。显然,元素1、2、3、5、6的总出现次数都为2,而我们是从左到右遍历的,故这一步的选择列号为1.
2、找出所有含有元素1的子集,即A和B,也就是第一行和第二行。现在S={}
3、我们先考虑子集A、先尝试将子集A加入到解集S中。现在S={A},
4、现在将所有和A有交集的子集从矩阵中删除。具体做法是,子集A对应3个列1,4,7。那么从矩阵中把所有在这3列中含1的行删掉。
比如对第1列,含1的行有A,B;对第4列,含1的行有A,B,C;对第7列,含1的行有A,C,E,F。 因此删除的就是A 、B、 C、 E 、F等5行
5、删除所有和A有交集的列,即第1、4、7列。
现在矩阵的状态为
2 3 5 6
D 0 1 1 1
6、由于现在的矩阵只剩下1行,且第2列为0,这意味着,当前解S={A,D}的并集中肯定不含元素2,
所以S={A,D}不是一个可行解,因此要回退到第2步的状态,然后从子集B开始求解
7、回退到步骤2将子集B加入到S中,S={B}
8、将所有和B有交集的行和列从矩阵中删除。即删除行A,B,C和列1,4,删除之后,矩阵M的新状态为
2 3 5 6 7
D 0 1 1 1 0
E 1 1 0 1 1
F 1 0 0 0 1
9、找出目前M中含1最少的列,显然这一步得到的列号为5.
10、和第5列有交集的行(即含元素5的子集)只有D。
11、将D加入到解集中,现在S={B,D}
12、删除所有和D相交的行和列,于是删除了列3,5,6和行D、E,现在矩阵的状态为
2 7
F 1 1
13、由于现在剩下的是全1行,因此把F加入到解集中,S={B,D,F},现在,容易验证这是一个可行解,算法结束
#include <cstdio> #include <cstring> const int MAXR = 20; const int MAXC = 310; const int MAXN = MAXR * MAXC + MAXC; const int INF = MAXR * 10; int n, m; int L[MAXN], R[MAXN], U[MAXN], D[MAXN]; int C[MAXN], O[MAXN], S[MAXN], H[MAXR]; int nodeNumber; void init() { for(int i=0;i<=m;++i) { L[i] = i - 1; R[i] = i + 1; U[i] = i; D[i] = i; C[i] = i; O[i] = 0; S[i] = 0; } L[0] = m; R[m] = 0; nodeNumber = m + 1; memset(H, 0, sizeof(H)); } void insert(int i, int j) { if(H[i]) //判断这一行中有没有节点 { L[nodeNumber] = L[H[i]]; //如果有节点了,就添加一个节点,并把左指针指向第一个节点的未被更新的左指针,也就是新节点的左指针 R[nodeNumber] = H[i]; //右指针指向该行第一个节点 L[R[nodeNumber]] = nodeNumber; //更新第一个节点的左指针 R[L[nodeNumber]] = nodeNumber; //更新前一个节点的右指针 } else { L[nodeNumber] = nodeNumber; //如果没有节点就添加一个节点,并把左右指针指向自己 R[nodeNumber] = nodeNumber; H[i] = nodeNumber; //标记为该行第一个节点 } U[nodeNumber] = U[j]; //节点的上指针指向上面一个节点 D[nodeNumber] = j; //节点的下指针指向对应的列表头 U[D[nodeNumber]] = nodeNumber; //更新列表头的上指针指向当前节点 D[U[nodeNumber]] = nodeNumber; //更新上一个节点的下指针指向当前节点 C[nodeNumber] = j; //记录列号 O[nodeNumber] = i; //记录行号 ++ S[j]; //S当中记录着每列节点的个数 ++ nodeNumber; //新建一个节点 } 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[C[j]]; //把相应列上对应的节点数也减少1个 } } } void resume(int c) { for(int i=U[c];i!=c;i=U[i]) //从该列最后一个节点往上遍历,不遍历列表头节点 { for(int j=L[i];j!=i;j=L[j]) //从该行最后一个节点往左遍历,不遍历第一个节点 { ++ S[C[j]]; //列上面恢复一个节点,节点数也+1 D[U[j]] = j; //恢复行 U[D[j]] = j; } } R[L[c]] = c; //最后恢复列 L[R[c]] = c; } bool dfs(int k) { if(!R[0]) //如果列表头上第一个节点的右指针为0,即所有列都被删除,则搜索完成 { return true; } //因为要输出最优秀(最少的行)的答案,每次都要优先搜索列节点最少的列 int count = INF, c; for(int i=R[0];i;i=R[i]) //从第一个列开始,直到右指针指向列头,即逐列遍历 { if(S[i] < count) //找到节点最少的列 { count = S[i]; //count里面放最少的节点数 c = i; //把该列做标记 ,选择了该列 if(1 == count) //该列节点,为最少允许的情况直接算是找到了,跳出 { break; } } } remove(c); //先将这一列中有1的格子所在的行全部删除 for(int i=D[c];i!=c;i=D[i]) //对这一列上有1的每一行进行枚举 { for(int j=R[i];j!=i;j=R[j]) //枚举到第i行时,将改行上所有有1的列j全部删除 { remove(C[j]); //如果行上有符合要求的列,删了。因为精确覆盖不能重复,不能选它们了 } if(dfs(k+1)) //递归层数+1,深度搜索 { return true; } for(int j=L[i];j!=i;j=L[j]) //从该行最后一个节点往左遍历,第一个节点不遍历 { resume(C[j]); //恢复之前删除的*行* } } resume(c); //递归跳出,恢复之前删除的列 return false; } int main() { int t; while(~scanf("%d%d",&n,&m)) { init(); /* printf("L R U D C O "); for(int i=0;i<=m;i++) { printf("%d ",L[i]); printf("%d ",R[i]); printf("%d ",U[i]); printf("%d ",D[i]); printf("%d ",C[i]); printf("%d ",O[i]); } */ for(int i=1;i<=n;++i) { for(int j=1;j<=m;++j) { scanf("%d", &t); if(t) { insert(i, j); //建立抽象十字链表 } } } bool flag = true; for(int i=1;i<=m;++i) { if(S[i] == 0) //如果有一列没有一个节点,直接失败 { flag = false; break; } } if(flag && dfs(0)) //进入深度搜索 { printf("Yes, I found it "); } else { printf("It is impossible "); } } return 0; } /* 6 7 0 0 1 0 1 1 0 1 0 0 1 0 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 1 0 1 */