POJ_3740 Easy Finding ——精确覆盖问题,DLX模版
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\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 */
转载于:https://www.cnblogs.com/caiyishuai/p/9019036.html
POJ_3740 Easy Finding ——精确覆盖问题,DLX模版相关推荐
- DLX (Dancing Links/舞蹈链)算法——求解精确覆盖问题
精确覆盖问题的定义:给定一个由0-1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1 例如:如下的矩阵 就包含了这样一个集合(第1.4.5行) 如何利用给定的矩阵求出相应的行的集合 ...
- DLX精确覆盖 hdu4069 Squiggly Sudoku
传送门:点击打开链接 题意:将9*9的棋盘分割成了9个部分,每个部分都是9个格子,然后现在要求每个部分的数字恰是1~9的排列,每一行每一列恰是1~9的排列,问是否有解,有多少组解,如果只有1组解打印出 ...
- ACM练级日志:HDU 4735(ACM 成都网络赛) 重复覆盖与DLX
今天费了一下午+一晚上的劲,终于把重复覆盖问题给解决了.作为这算法的牺牲品的就是成都网络赛让我知道DLX这东西存在的那道题,HDU 4735.这也是第一次尝试独立对问题构造矩阵然后调用DLX得出结果的 ...
- poj 3740 Easy Finding
2019独角兽企业重金招聘Python工程师标准>>> 第一道dancing links,纪念下~ Easy Finding Time Limit: 1000MS Memory Li ...
- HDOJ 4069 Squiggly Sudoku 精确覆盖+搜索
//HDOJ 4069 Squiggly Sudoku 精确覆盖+搜索 /* 题意:数独变形 9块弯弯曲曲的区域 每块有9个小格往这81个格子里面填写1-9的数字,使得每行,每列,每个区域都含有1.2 ...
- HDU 4069 Squiggly Sudoku【Dancing Links精确覆盖】
跟普通的数独有一点点不同,先预处理一下再用Dancing Links进行精确覆盖即可. #include <iostream> #include <cstdio> #inclu ...
- CTF PWN之精确覆盖变量数据
刚开始接触pwn的朋友在做pwn练习时可能会有这样的疑问,怎么做到精确覆盖变量数据呢? 我们做pwn练习之前需要先知道:命令行参数C语言的main函数拥有两个参数,为int类型的argc参数,以及ch ...
- CTF-PWN练习之精确覆盖变量数据
目录 预备知识 一.相关实验 二.命令行参数 三.xargs命令 四.字节序 实验目的 实验环境 实验步骤一 源码审计 实验步骤二 使用gdb调试程序 实验步骤三 发起溢出攻击 预备知识 本实验要求实 ...
- 跳跃的舞者,舞蹈链(Dancing Links)算法——求解精确覆盖问题
精确覆盖问题的定义:给定一个由0-1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1 例如:如下的矩阵 就包含了这样一个集合(第1.4.5行) 如何利用给定的矩阵求出相应的行的集合 ...
- HDU 4398 whosyourdaddy 精确覆盖,允许重复覆盖
题目大意:有n个点,其中一些点是相连的.领主的攻击具有溅射,即攻击一个点,此点相邻的点也会收到攻击.问,领主最少攻击多少次,使得每个点都至少被攻击一次. 与传统精确覆盖相比,此题允许重复覆盖.那么,我 ...
最新文章
- 图灵十二月书讯 ——年底大餐
- 数学建模入门例题python_[Python与数学建模-入门使用]-2Python基础知识
- 解密:面部特征点检测的关键技术
- Python 字典中get() 函数
- 【学习笔记】观察者模式
- 台式计算机单核与双核,什么是单核cpu、双核cpu 单核cpu和双核cpu的区别是什么...
- java学习(44):引用参数传递
- 数字图像处理--图像颜色
- OAuth2.0学习(1-7)授权方式4-客户端模式(Client Credentials Grant)
- (day 16 - 双指针)剑指 Offer 35. 复杂链表的复制
- mysql 帮助文档使用
- Studio 3T过期了的解决办法,亲测有效
- deebot扫地机器人说明书_ecovacs扫地机器人730使用说明书_deebot扫地机器说明书
- 一副眼镜一千多贵吗_眼镜片的价格差距为什么那么大
- 观点 | 如何让智慧城市这头巨象跳芭蕾?
- 超微服务器安装Linux,超微服务器使用IPMI安装操作系统
- 手机wps怎么设置打印横竖_手机WPS怎么设置横版打印?
- LeetCode 71-80题
- mysql修改字段设置_在mysql中,如何改变列声明.
- 雇佣兵的战斗力最大可以到达多少
热门文章
- 2021-08-04 Mysql自连接
- 控制器布局 php,PhalconPHP视图/布局/控制器
- 凯立德地图导航2020年最新版车载_高精度地图会把自动驾驶带跑偏吗?
- set 排序_堆排序C++实现
- PHP 二维数组根据某个字段排序
- mysql+mmm+主动模式_MySQL集群搭建(4)-MMM+LVS+Keepalived
- zeal刷新不出来_热血传奇:计算怪物刷新时间,升级速度立马不同,老玩家笑出了声。...
- [Errno 256] No more mirrors to try.
- Dubbo 优雅停机演进之路
- Lucene 文档检索 详细说明