标签:count str 标记 sample 不用 BMI number let als
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
1、从矩阵中选择一行
2、根据定义,标示矩阵中其他行的元素
3、删除相关行和列的元素,得到新矩阵
4、如果新矩阵是空矩阵,并且之前的一行都是1,那么求解结束,跳转到6;新矩阵不是空矩阵,继续求解,跳转到1;新矩阵是空矩阵,之前的一行中有0,跳转到5
5、说明之前的选择有误,回溯到之前的一个矩阵,跳转到1;如果没有矩阵可以回溯,说明该问题无解,跳转到7
6、求解结束,把结果输出
7、求解结束,输出无解消息
Dancing Links用的数据结构是交叉十字循环双向链
而Dancing Links中的每个元素不仅是横向循环双向链中的一份子,又是纵向循环双向链的一份子。
因为精确覆盖问题的矩阵往往是稀疏矩阵(矩阵中,0的个数多于1),Dancing Links仅仅记录矩阵中值是1的元素。
#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); //把该列和列上符合要求的行删除 for(int i=D[c];i!=c;i=D[i]) //在被删除的列上,往下遍历 { for(int j=R[i];j!=i;j=R[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\tR\tU\tD\tC\tO\n"); for(int i=0;i<=m;i++) { printf("%d\t",L[i]); printf("%d\t",R[i]); printf("%d\t",U[i]); printf("%d\t",D[i]); printf("%d\t",C[i]); printf("%d\t\n",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\n"); } else { printf("It is impossible\n"); } } 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 */
POJ_3740 Easy Finding ——精确覆盖问题,DLX模版
标签:count str 标记 sample 不用 BMI number let als
原文地址:https://www.cnblogs.com/caiyishuai/p/9019036.html