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 = ;
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模版的更多相关文章
- Easy Finding POJ - 3740 (DLX)
显然这是一道dfs简单题 或许匹配也能做 然而用了dancing links 显然这也是一道模板题 好的吧 调了一上午 终于弄好了模板 Easy Finding Time Limit: 1000MS ...
- poj 3740 Easy Finding 精确匹配
题目链接 dlx的第一题, 真是坎坷..... #include <iostream> #include <vector> #include <cstdio> #i ...
- 【转】DLX 精确覆盖 重复覆盖
问题描述: 给定一个n*m的矩阵,有些位置为1,有些位置为0.如果G[i][j]==1则说明i行可以覆盖j列. Problem: 1)选定最少的行,使得每列有且仅有一个1. 2)选定最少的行,使得每列 ...
- dancing link 精确覆盖 重复覆盖 (DLX)
申明:因为转载的没有给出转载链接,我就把他的链接附上,请尊重原创: http://www.cnblogs.com/-sunshine/p/3358922.html 如果谁知道原创链接 给一下,请尊重原 ...
- DLX 舞蹈链 精确覆盖 与 重复覆盖
精确覆盖问题:给定一个由0-1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1 还有重复覆盖问题 dancing links 是 一种数据结构,用来优化搜索,不算是一种算法.(双向 ...
- POJ 3074 Sudoku DLX精确覆盖
DLX精确覆盖.....模版题 Sudoku Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 8336 Accepted: ...
- HDU 3957 Street Fighter(搜索、DLX、重复覆盖+精确覆盖)
很久以前就看到的一个经典题,一直没做,今天拿来练手.街霸 给n<=25个角色,每个角色有 1 or 2 个版本(可以理解为普通版以及爆发版),每个角色版本可以KO掉若干人. 问最少选多少个角色( ...
- zoj 3209.Treasure Map(DLX精确覆盖)
直接精确覆盖 开始逐行添加超时了,换成了单点添加 #include <iostream> #include <cstring> #include <cstdio> ...
- zoj - 3209 - Treasure Map(精确覆盖DLX)
题意:一个 n x m 的矩形(1 <= n, m <= 30),现给出这个矩形中 p 个(1 <= p <= 500)子矩形的左下角与右下角坐标,问最少用多少个子矩形能够恰好 ...
随机推荐
- Android 通过名称获取资源ID
当我们获取网络数据的时候,解析之后往往都是一个字符串,而不是资源id,所有我们没有办法直接使用,只能通过名称来获取到资源id, package com.example.administrator.de ...
- 真相大白:为什么Entity Framework不能进行跨数据库查询(附解决方法)
在上篇随笔中,我们发现Entity Framework在构建SQL语句时,将ToTable("CNBlogsTex.dbo.blog_PostBody")中的"CNBlo ...
- 【疯狂labview】 Xcontrol+LVoop封装练习 Toolbar
labview没有toolbar,并且没有可复用的 控件 其实项目中很需要 特别是在框架平台的搭建上, 本文以Xcontrol用PICTURE的方式实现toolbar,并用LVoop封装 废话少说 直 ...
- BZOJ3528: [Zjoi2014]星系调查
唉,看到这题直接想起自己的Day1,还是挺难受的,挺傻一题考试的时候怎么就没弄出来呢…… 这两天CP让我给他写个题解,弄了不是很久就把这个题给弄出来了,真不知道考试的时候在干嘛. 明天就出发去北京了, ...
- sql如何分组选择显示最新的一条数据
怎样在数据库的一个表里筛选出每一人的时间最新的一条记录?用SQL语句 wenchuan408 wenchuan408 结帖率:100% sql数据库 yhh name ...
- 蓝牙(cc2540) 协议栈 学习一
---------------------------------------------------------- app ------------------------------------- ...
- mcake活动维护,检查 ★ ★
一.检查火狐浏览器.chrome浏览器.ie8.9.10.11是否显示正常 二.对比设计稿 三.动画添加 四.检查图片是否失真 五.ie8空按钮无法点击 六.官网banner制作
- Java基础学习-代码块
/*代码块: * 用{}修饰的代码 * 1.局部代码块:控制变量,存在方法中,控制变量的生命周期(作用域) * 2.构造代码块:提取构造方法中的共性,每次创建对象,都会执行,并且在构造方法执行之前执行 ...
- New Concept English three (43)
30 54 Insurance companies are normally willing to insure anything. Insuring public or private proper ...
- build.prop文件介绍与用法举例
build.prop 是一个属性文件,在Android系统中.prop文件很重要,记录了系统的设置和改变 以下是修改教程及一些build.prop参数的中英文对照解释,修改前,注意先备份原build. ...