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

 

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 = ;
const int MAXC = ;
const int MAXN = MAXR * MAXC + MAXC;
const int INF = MAXR * ; 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=;i<=m;++i)
{
L[i] = i - ;
R[i] = i + ;
U[i] = i;
D[i] = i;
C[i] = i;
O[i] = ;
S[i] = ;
}
L[] = m;
R[m] = ;
nodeNumber = m + ;
memset(H, , 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,即所有列都被删除,则搜索完成
{
return true;
}
//因为要输出最优秀(最少的行)的答案,每次都要优先搜索列节点最少的列
int count = INF, c;
for(int i=R[];i;i=R[i]) //从第一个列开始,直到右指针指向列头,即逐列遍历
{
if(S[i] < count) //找到节点最少的列
{
count = S[i]; //count里面放最少的节点数
c = i; //把该列做标记 ,选择了该列
if( == 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,深度搜索
{
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=;i<=n;++i)
{
for(int j=;j<=m;++j)
{
scanf("%d", &t);
if(t)
{
insert(i, j); //建立抽象十字链表
}
}
}
bool flag = true;
for(int i=;i<=m;++i)
{
if(S[i] == ) //如果有一列没有一个节点,直接失败
{
flag = false;
break;
}
}
if(flag && dfs()) //进入深度搜索
{
printf("Yes, I found it\n");
}
else
{
printf("It is impossible\n");
}
}
return ;
}
/*
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模版的更多相关文章

  1. Easy Finding POJ - 3740 (DLX)

    显然这是一道dfs简单题 或许匹配也能做 然而用了dancing links 显然这也是一道模板题 好的吧 调了一上午 终于弄好了模板 Easy Finding Time Limit: 1000MS ...

  2. poj 3740 Easy Finding 精确匹配

    题目链接 dlx的第一题, 真是坎坷..... #include <iostream> #include <vector> #include <cstdio> #i ...

  3. 【转】DLX 精确覆盖 重复覆盖

    问题描述: 给定一个n*m的矩阵,有些位置为1,有些位置为0.如果G[i][j]==1则说明i行可以覆盖j列. Problem: 1)选定最少的行,使得每列有且仅有一个1. 2)选定最少的行,使得每列 ...

  4. dancing link 精确覆盖 重复覆盖 (DLX)

    申明:因为转载的没有给出转载链接,我就把他的链接附上,请尊重原创: http://www.cnblogs.com/-sunshine/p/3358922.html 如果谁知道原创链接 给一下,请尊重原 ...

  5. DLX 舞蹈链 精确覆盖 与 重复覆盖

    精确覆盖问题:给定一个由0-1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1 还有重复覆盖问题 dancing links 是 一种数据结构,用来优化搜索,不算是一种算法.(双向 ...

  6. POJ 3074 Sudoku DLX精确覆盖

    DLX精确覆盖.....模版题 Sudoku Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 8336   Accepted: ...

  7. HDU 3957 Street Fighter(搜索、DLX、重复覆盖+精确覆盖)

    很久以前就看到的一个经典题,一直没做,今天拿来练手.街霸 给n<=25个角色,每个角色有 1 or 2 个版本(可以理解为普通版以及爆发版),每个角色版本可以KO掉若干人. 问最少选多少个角色( ...

  8. zoj 3209.Treasure Map(DLX精确覆盖)

    直接精确覆盖 开始逐行添加超时了,换成了单点添加 #include <iostream> #include <cstring> #include <cstdio> ...

  9. zoj - 3209 - Treasure Map(精确覆盖DLX)

    题意:一个 n x m 的矩形(1 <= n, m <= 30),现给出这个矩形中 p 个(1 <= p <= 500)子矩形的左下角与右下角坐标,问最少用多少个子矩形能够恰好 ...

随机推荐

  1. Android 通过名称获取资源ID

    当我们获取网络数据的时候,解析之后往往都是一个字符串,而不是资源id,所有我们没有办法直接使用,只能通过名称来获取到资源id, package com.example.administrator.de ...

  2. 真相大白:为什么Entity Framework不能进行跨数据库查询(附解决方法)

    在上篇随笔中,我们发现Entity Framework在构建SQL语句时,将ToTable("CNBlogsTex.dbo.blog_PostBody")中的"CNBlo ...

  3. 【疯狂labview】 Xcontrol+LVoop封装练习 Toolbar

    labview没有toolbar,并且没有可复用的 控件 其实项目中很需要 特别是在框架平台的搭建上, 本文以Xcontrol用PICTURE的方式实现toolbar,并用LVoop封装 废话少说 直 ...

  4. BZOJ3528: [Zjoi2014]星系调查

    唉,看到这题直接想起自己的Day1,还是挺难受的,挺傻一题考试的时候怎么就没弄出来呢…… 这两天CP让我给他写个题解,弄了不是很久就把这个题给弄出来了,真不知道考试的时候在干嘛. 明天就出发去北京了, ...

  5. sql如何分组选择显示最新的一条数据

    怎样在数据库的一个表里筛选出每一人的时间最新的一条记录?用SQL语句 wenchuan408 wenchuan408    结帖率:100% sql数据库 yhh              name  ...

  6. 蓝牙(cc2540) 协议栈 学习一

    ---------------------------------------------------------- app ------------------------------------- ...

  7. mcake活动维护,检查 ★ ★

    一.检查火狐浏览器.chrome浏览器.ie8.9.10.11是否显示正常 二.对比设计稿 三.动画添加 四.检查图片是否失真 五.ie8空按钮无法点击 六.官网banner制作

  8. Java基础学习-代码块

    /*代码块: * 用{}修饰的代码 * 1.局部代码块:控制变量,存在方法中,控制变量的生命周期(作用域) * 2.构造代码块:提取构造方法中的共性,每次创建对象,都会执行,并且在构造方法执行之前执行 ...

  9. New Concept English three (43)

    30 54 Insurance companies are normally willing to insure anything. Insuring public or private proper ...

  10. build.prop文件介绍与用法举例

    build.prop 是一个属性文件,在Android系统中.prop文件很重要,记录了系统的设置和改变 以下是修改教程及一些build.prop参数的中英文对照解释,修改前,注意先备份原build. ...