Easy Finding
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 18790   Accepted: 5184

Description

Given a M×N matrix AAij ∈ {0, 1} (0 ≤ i < M, 0 ≤ j < N), could you find some rows that let every cloumn contains and only contains one 1.

Input

There are multiple cases ended by EOF. Test case up to 500.The first line of input is MN (M ≤ 16, N ≤ 300). The next M lines every line contains N integers separated by space.

Output

For each test case, if you could find it output "Yes, I found it", otherwise output "It is impossible" per line.

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

POJ Monthly Contest - 2009.08.23, MasterLuo

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模版相关推荐

  1. DLX (Dancing Links/舞蹈链)算法——求解精确覆盖问题

    精确覆盖问题的定义:给定一个由0-1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1 例如:如下的矩阵 就包含了这样一个集合(第1.4.5行) 如何利用给定的矩阵求出相应的行的集合 ...

  2. DLX精确覆盖 hdu4069 Squiggly Sudoku

    传送门:点击打开链接 题意:将9*9的棋盘分割成了9个部分,每个部分都是9个格子,然后现在要求每个部分的数字恰是1~9的排列,每一行每一列恰是1~9的排列,问是否有解,有多少组解,如果只有1组解打印出 ...

  3. ACM练级日志:HDU 4735(ACM 成都网络赛) 重复覆盖与DLX

    今天费了一下午+一晚上的劲,终于把重复覆盖问题给解决了.作为这算法的牺牲品的就是成都网络赛让我知道DLX这东西存在的那道题,HDU 4735.这也是第一次尝试独立对问题构造矩阵然后调用DLX得出结果的 ...

  4. poj 3740 Easy Finding

    2019独角兽企业重金招聘Python工程师标准>>> 第一道dancing links,纪念下~ Easy Finding Time Limit: 1000MS Memory Li ...

  5. HDOJ 4069 Squiggly Sudoku 精确覆盖+搜索

    //HDOJ 4069 Squiggly Sudoku 精确覆盖+搜索 /* 题意:数独变形 9块弯弯曲曲的区域 每块有9个小格往这81个格子里面填写1-9的数字,使得每行,每列,每个区域都含有1.2 ...

  6. HDU 4069 Squiggly Sudoku【Dancing Links精确覆盖】

    跟普通的数独有一点点不同,先预处理一下再用Dancing Links进行精确覆盖即可. #include <iostream> #include <cstdio> #inclu ...

  7. CTF PWN之精确覆盖变量数据

    刚开始接触pwn的朋友在做pwn练习时可能会有这样的疑问,怎么做到精确覆盖变量数据呢? 我们做pwn练习之前需要先知道:命令行参数C语言的main函数拥有两个参数,为int类型的argc参数,以及ch ...

  8. CTF-PWN练习之精确覆盖变量数据

    目录 预备知识 一.相关实验 二.命令行参数 三.xargs命令 四.字节序 实验目的 实验环境 实验步骤一 源码审计 实验步骤二 使用gdb调试程序 实验步骤三 发起溢出攻击 预备知识 本实验要求实 ...

  9. 跳跃的舞者,舞蹈链(Dancing Links)算法——求解精确覆盖问题

    精确覆盖问题的定义:给定一个由0-1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1 例如:如下的矩阵 就包含了这样一个集合(第1.4.5行) 如何利用给定的矩阵求出相应的行的集合 ...

  10. HDU 4398 whosyourdaddy 精确覆盖,允许重复覆盖

    题目大意:有n个点,其中一些点是相连的.领主的攻击具有溅射,即攻击一个点,此点相邻的点也会收到攻击.问,领主最少攻击多少次,使得每个点都至少被攻击一次. 与传统精确覆盖相比,此题允许重复覆盖.那么,我 ...

最新文章

  1. 图灵十二月书讯 ——年底大餐
  2. 数学建模入门例题python_[Python与数学建模-入门使用]-2Python基础知识
  3. 解密:面部特征点检测的关键技术
  4. Python 字典中get() 函数
  5. 【学习笔记】观察者模式
  6. 台式计算机单核与双核,什么是单核cpu、双核cpu 单核cpu和双核cpu的区别是什么...
  7. java学习(44):引用参数传递
  8. 数字图像处理--图像颜色
  9. OAuth2.0学习(1-7)授权方式4-客户端模式(Client Credentials Grant)
  10. (day 16 - 双指针)剑指 Offer 35. 复杂链表的复制
  11. mysql 帮助文档使用
  12. Studio 3T过期了的解决办法,亲测有效
  13. deebot扫地机器人说明书_ecovacs扫地机器人730使用说明书_deebot扫地机器说明书
  14. 一副眼镜一千多贵吗_眼镜片的价格差距为什么那么大
  15. 观点 | 如何让智慧城市这头巨象跳芭蕾?
  16. 超微服务器安装Linux,超微服务器使用IPMI安装操作系统
  17. 手机wps怎么设置打印横竖_手机WPS怎么设置横版打印?
  18. LeetCode 71-80题
  19. mysql修改字段设置_在mysql中,如何改变列声明.
  20. 雇佣兵的战斗力最大可以到达多少

热门文章

  1. 2021-08-04 Mysql自连接
  2. 控制器布局 php,PhalconPHP视图/布局/控制器
  3. 凯立德地图导航2020年最新版车载_高精度地图会把自动驾驶带跑偏吗?
  4. set 排序_堆排序C++实现
  5. PHP 二维数组根据某个字段排序
  6. mysql+mmm+主动模式_MySQL集群搭建(4)-MMM+LVS+Keepalived
  7. zeal刷新不出来_热血传奇:计算怪物刷新时间,升级速度立马不同,老玩家笑出了声。...
  8. [Errno 256] No more mirrors to try.
  9. Dubbo 优雅停机演进之路
  10. Lucene 文档检索 详细说明