#舞蹈链(DLX) Tags:搜索 ##作业部落 ##评论地址

##一、概述 特别特别感谢这位童鞋His blog 舞蹈链是一种优美的搜索,就像下面这样跳舞~

舞蹈链用于解决精确覆盖或者重复覆盖的问题 你可以想象成贪吃蛇的一个上下左右联通的地图 $Dancing Links$就是通过链表这样实现的 网上有图的博客

##二、实现 ####更详细的讲解在课件中 ###精确覆盖 精确覆盖大概指的就是数独和八皇后那样的问题 矩阵中选择一个行的集合,使得每列有且只有一个1 那么就是说每个格子上的点都有若干限制条件(行、列、对角线),每个条件都只允许一个元素 在舞蹈链中(可以把它看作一个表格),每个元素看作一行,限制条件转化为列,选一行删去也同时要删去这一行中所有点所在的列 然后舞蹈链兹瓷快速删除这些东西和快速回溯(复杂度未知) 大概有$init$、$link$、$remove$、$resume$、$dance$五个函数 实现的话看代码吧,有详细的注释 ###Code - [luogu1219]八皇后

#include

#include

#include

#include

using namespace std;

const int N=100100;

int ans,nn,o;

struct out{int a[14];}Ans[N];

namespace DLX

{

int n,m,cnt;//长宽,点的数量

int l[N],r[N],u[N],d[N];//上下左右的情况

int row[N],col[N];//每个点所处的行列

int h[N],s[N];//头节点和每列节点数

int ansk[20];//答案

void init(int nn,int mm)

{

//这个表格被循环套了起来,就像贪吃蛇的地图,左右和上下相通

//预先给第0行的每一列弄一个点

n=nn,m=mm;

for(int i=0;i<=m;i++)

r[i]=i+1,l[i]=i-1,u[i]=d[i]=i;

r[m]=0;l[0]=cnt=m;

memset(h,-1,sizeof(h));

}

void link(int R,int C)//在R行C列插入点

{

s[C]++;cnt++;//先记录这个点的各种信息

row[cnt]=R; col[cnt]=C;

//把列的链表改动

u[cnt]=C;

d[cnt]=d[C];

u[d[C]]=cnt;

d[C]=cnt;

//把行的链表改动

if(h[R]<0) h[R]=l[cnt]=r[cnt]=cnt;

else

{

r[cnt]=h[R];

l[cnt]=l[h[R]];

r[l[h[R]]]=cnt;

l[h[R]]=cnt;

}

}

void remove(int C)//删除C列以及C列上有点的行

{

r[l[C]]=r[C]; l[r[C]]=l[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[col[j]]--;//是减得只剩下1吗(dei)

}

}

void resume(int C)//恢复C列以及C列上有点的行

{

r[l[C]]=C; l[r[C]]=C;

for(int i=d[C];i!=C;i=d[i])

for(int j=r[i];j!=i;j=r[j])

{

u[d[j]]=j;

d[u[j]]=j;

s[col[j]]++;

}

}

void dance(int deep)

{

int C=r[0];//找第一个限制条件

if(C>2*nn)//如果所有的行已经被删完就统计答案(能不能>2n)

{

ans++;

for(int i=0,x,y;i

{

//记录下来选的点的编号,用编号还原行列

x=ansk[i]%nn;

y=(ansk[i]-1)/nn+1;

if(x==0) x=nn;

Ans[ans].a[y]=x;//x和y是等价的,可以交换

}

return;

}

for(int i=C;i<=nn;i=r[i])//找到点最少的列

/*

这是一处剪枝,因为删掉点最少的列,就是为了满足这个限制条件

需要枚举删掉的点就少一些,从而使得之后的剪枝更高效

相当于把搜索树繁茂的地方留给叶子,而深度越深越容易被剪枝

不加会T

*/

if(s[i]

remove(C);//删掉这一列

for(int i=d[C];i!=C;i=d[i])//枚举答案是这一列的哪个点,因为每一列只能选一个点,所以枚举选哪个

{

ansk[deep]=row[i];//记录答案,这个点编号是row[i]

for(int j=r[i];j!=i;j=r[j]) remove(col[j]);//这个点的行也得删了,把这行有点的列也删掉

dance(deep+1);

for(int j=r[i];j!=i;j=r[j]) resume(col[j]);//回溯

}

resume(C);//回溯过程

}

}

int cmp(const out&A,const out&B)

{

int p=0;while(A.a[p]==B.a[p]) p++;

return A.a[p]

}

int main()

{

///freopen("a.out","w",stdout);

scanf("%d",&nn);

/*

nn*nn个格子,每个格子看作舞蹈链的一行

总共有nn行nn列nn×2-1左对角nn×2-1右对角 共6×nn-2个限制

把每个限制看作一列,进行精准覆盖

*/

DLX::init(nn*nn,6*nn-2);

for(int i=1;i<=nn;i++)

for(int j=1;j<=nn;j++)

{

o++;

DLX::link(o,i);//占据第i行

DLX::link(o,j+nn);//占据第j列(能不能不写这一句)

DLX::link(o,i-j+3*nn);//占据第i-j+nn个左上到右下的对角线

DLX::link(o,i+j+4*nn-2);//占据第i+j-1个右上到左下的对角线

}

DLX::dance(0);//跳舞啦

sort(Ans+1,Ans+ans+1,cmp);

for(int i=1;i<=3;i++,puts(""))

for(int j=1;j<=nn;j++) printf("%d ",Ans[i].a[j]);

printf("%d\n",ans);return 0;

}

###Code - Easy Finding戳我 ###重复覆盖 矩阵中选择一个行的集合,使得每列至少有一个1 所以选了一列之后不能把列中有1的所有的行删掉,复杂度会提高,加一个估价函数的$A*$剪枝 ###Code - [FZU1686]神龙的难题

#include

#include

#include

#include

using namespace std;

const int N=70000;

int A[20][20],n,m,n1,m1;

int o,ans,tt;

namespace DLX

{

int n,m,p,u[N],d[N],l[N],r[N];

int col[N],row[N],h[300],s[300],vis[300];

void init(int nn,int mm)

{

n=nn,m=mm;

for(int i=0;i<=m;i++)

l[i]=i-1,r[i]=i+1,d[i]=u[i]=i,s[i]=0;

p=m;l[0]=m;r[m]=0;

memset(h,-1,sizeof(h));

}

void link(int R,int C)

{

p++;row[p]=R;col[p]=C;s[C]++;

d[p]=C;u[p]=u[C];

d[u[C]]=p;u[C]=p;

if(h[R]<0) h[R]=l[p]=r[p]=p;

else r[p]=h[R],l[p]=l[h[R]],r[l[h[R]]]=p,l[h[R]]=p;

}

void remove(int C)

{

for(int i=d[C];i!=C;i=d[i])

l[r[i]]=l[i],r[l[i]]=r[i];

}

void resume(int C)

{

for(int i=u[C];i!=C;i=u[i])

l[r[i]]=i,r[l[i]]=i;

}

int H()

{

int res=0;

memset(vis,0,sizeof(vis));

for(int i=r[0];i;i=r[i])

{

if(vis[i]) continue;

vis[i]=1; res++;

for(int j=d[i];j!=i;j=d[j])

for(int k=r[j];k!=j;k=r[k])

vis[col[k]]=1;

}

return res;

}

void dance(int step)

{

if(step+H()>=ans) return;

if(r[0]==0) {ans=min(ans,step);return;}

int C=r[0];

for(int i=r[C];i;i=r[i]) if(s[i]

for(int i=d[C];i!=C;i=d[i])

{

remove(i);

for(int j=r[i];j!=i;j=r[j]) remove(j);

dance(step+1);

for(int j=l[i];j!=i;j=l[j]) resume(j);

resume(i);

}

}

}

int main()

{

while(~scanf("%d%d",&n,&m))

{

o=1;ans=1e9;tt=0;

for(int i=1;i<=n;i++)

for(int j=1,x;j<=m;j++)

{

scanf("%d",&x);

if(x) A[i][j]=++tt;

else A[i][j]=0;

}

scanf("%d%d",&n1,&m1);

DLX::init((n-n1+1)*(m-m1+1),tt);

for(int i=1;i<=n-n1+1;i++)

for(int j=1;j<=m-m1+1;j++,o++)

for(int x=i;x<=i+n1-1;x++)

for(int y=j;y<=j+m1-1;y++)

if(A[x][y]) DLX::link(o,A[x][y]);

DLX::dance(0);printf("%d\n",ans);

}

}

##三、尾声 舞蹈链的复杂度是指数级别的,但是由于有非常强大的剪枝所以可以有玄学复杂度

在一般竞赛中舞蹈链并没有很广泛的应用和考察 但是这种思想需要大家了解,体会其中舞蹈的优美

弄个题单吧

舞蹈链java实现_舞蹈链(DLX) - osc_kpp7htz3的个人空间 - OSCHINA - 中文开源技术交流社区...相关推荐

  1. emc存储java打开后报错_连接EMC存储系统 - osc_mk8rqvg4的个人空间 - OSCHINA - 中文开源技术交流社区...

    1.准备一台笔记本电脑,一根网线即可. 2.将网线一头连接笔记本电脑,另一头连接存储.(连接存储的一头应连接到有扳手图标的那一网口上) 3.配置IP地址 IP:128.221.1.254 子网掩码:2 ...

  2. 申请一个Java公众号怎么命名_警惕不规范的变量命名 - Java公众号_Kirito的技术分享的个人空间 - OSCHINA - 中文开源技术交流社区...

    就在最近,项目组开始强调开发规范了,今天分享一个变量名命名不规范的小案例,强调一下规范的重要性. Boolean变量名命名规范 16年底,阿里公开了<Java开发规范手册>,其中有一条便是 ...

  3. java绘制统计直方图取平均_统计学——直方图解析 - osc_lv8qb16y的个人空间 - OSCHINA - 中文开源技术交流社区...

    直方图(Histogram),又称质量分布图,是一种统计报告图, 由一系列高度不等的纵向条纹或线段表示数据分布的情况. 一般用 横轴表示数据类型,纵轴表示分布情况. 直方图是数值数据分布的精确图形表示 ...

  4. java运动员最佳配对_运动员最佳配对问题 - osc_y1pyjby5的个人空间 - OSCHINA - 中文开源技术交流社区...

    这道题可以看为排列数的一个典型模块 一.算法实现题: 1.问题描述: 羽毛球队有男女运动员各n人,给定2个n×n矩阵P和Q.P[i][j]是男运动员i和女运动员j配对组成混合双打的男运动员竞赛优势:Q ...

  5. java 投票算法_摩尔投票算法 - woshixin的个人空间 - OSCHINA - 中文开源技术交流社区...

    摩尔投票算法(Moore majority vote algorithm) 这个在wiki的介绍在https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_m ...

  6. java遮罩层_页面遮罩层 - javaalex的个人空间 - OSCHINA - 中文开源技术交流社区

    一. 基于ajax请求的遮罩层: $.ajax({ type:'POST', url:url, data:obj, dataType:'json', beforeSend: function () { ...

  7. java 日程日历_日程日历示例 - voxer的个人空间 - OSCHINA - 中文开源技术交流社区...

    这是用户利用do_Gridview和do_ListView及其它组件绘制的日历和任务,基本实现一个完整的在线日程管理功能 先看图,android和ios上的效果图如下: 我们可以看到通过deviceo ...

  8. java知识理论_JAVA理论知识 - OSC_rnoszD的个人空间 - OSCHINA - 中文开源技术交流社区...

    1. final关键字可以用来修饰什么?分别起什么作用? a) final可以修饰类,这样的类不能被继承. b) final可以修饰方法,这样的方法不能被重写. c) final可以修饰变量,这样的变 ...

  9. java诸神之战游戏_mj回溯算法 - osc_7bgz0no1的个人空间 - OSCHINA - 中文开源技术交流社区...

    1)js版本 ​ /** * 基本思路: * 回溯法: 先挑选中将,再依次拆接出3个,3个的...直到结束,那么说明当前可以胡牌 */ let mahjong = [ "1T", ...

最新文章

  1. 【剑指offer】整数中1出现的次数,C++实现
  2. const应用和作用
  3. Longest Common Prefix
  4. python多进程打印输出_多进程打印日志
  5. appinventor离线版下载_Chrome 离线安装包下载
  6. 多线程、多核技术是什么技术?
  7. java.util.function包下的四大Function
  8. 基于JavaScript的Web端股票价格查看器——大道
  9. 转载《蜗居》评论 自文学城
  10. 初窥Deno 1.0面纱
  11. 嵌入式开发:C程序员的5个C++ 技巧
  12. 二进制和十进制之间的转换
  13. 物理计算机主机ip在哪,查看电脑的物理地址_查看电脑的物理ip地址
  14. 一组数据,带你读懂“2021中国民营企业500强”背后深意
  15. 《软硬结合——从零打造物联网》
  16. 微信小程序支付签名生成(客户端)
  17. 方差、标准差和均方根误差的区别总结
  18. html语言计算圆周长和面积,纯JSP实现计算圆的面积和周长
  19. gitlab删除项目时 ,没有删除选项
  20. 详解如何获取深度学习模型中间层的输出值

热门文章

  1. MSP430 ADC_10bit
  2. 微信小程序倒计时,购物车,向左滑删除 左拉删除
  3. JavaScript中大于符号与小于符号的问题
  4. 公众账号取名的技巧与忌讳?
  5. Uboot sandbox
  6. 我想讲的,都在这个故事里
  7. 黑客盗走银行3亿比索|Rowhammer变体出现|DHS发布网络安全战略
  8. 6.4 差异可视化-多维量法(MDS)
  9. wireshark看不到网口
  10. 互联网日报 | 蛋壳公寓否认破产;滴滴发布首款定制网约车D1;京东Q3营收1742亿元超预期...