浅谈舞蹈链(DLX)

news/2024/9/20 4:23:37

名字:

\(DL\)\(Dancing\space Link\),舞蹈链,是由\(Donald\space Knuth\)提出的数据结构,用来优化 \(X\) 算法,所以叫\(DLX\)

\(X\)算法详解

用于求解精确覆盖问题,精确覆盖问题的定义:给定一个由0-1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1

例:

\(A,B,C \subseteq S\)

\(A \space \cap \space B= Ø\)\(A \space \cup \space B=S\)则集合\(A,B\)是问题的一种解

\(X\)算法求解模拟:

给出矩阵\(A\)

\[A=\left( \begin{matrix} 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 \end{matrix} \right) \]

选择第一列:

\[\left( \begin{matrix} 0&0&1&0&1&1&0\\\\\\\\\\ \end{matrix} \right) \]

将有\(1\)的列向下延伸,若该行有\(1\),标记该行,处理\(A\)矩阵,得到\(B\)矩阵

\[B=\left( \begin{matrix} 0&0&1&0&1&1&0\\&&0&&0&0\\0&1&1&0&0&1&0\\&&0&&0&0\\&&0&&0&0\\0&0&0&1&1&0&1 \end{matrix} \right) \]

\(S矩阵=A矩阵-B矩阵\)(删除所标记的行和列)

\[A_1=\left( \begin{matrix} 1&0&1&1\\1&0&1&0\\0&1&0&1 \end{matrix} \right) \]

再选择第一列

\[\left( \begin{matrix} 1&0&1&1\\\\\\ \end{matrix} \right) \]

标记

\[B_1= \left( \begin{matrix} 1&0&1&1\\1&&1&0\\0&1&0&1 \end{matrix} \right) \]

得到新矩阵,又得到一个规模较小的精确覆盖问题

\[S_1= \left( \begin{matrix} 0 \end{matrix} \right) \]

发现\(A\)矩阵不是空的,也没有一列有\(1\)(无法继续操作)

回溯

\[A_1=\left( \begin{matrix} 1&0&1&1\\1&0&1&0\\0&1&0&1 \end{matrix} \right) \]

不能尝试第一行,标记第二行,尝试继续拓展

\[B_2= \left( \begin{matrix} 1&0&1&1\\1&0&1&0\\0&&0 \end{matrix} \right) \]

同理可得

\[A_3= \left( \begin{matrix} 1&1 \end{matrix} \right) \]

\(A_3\)中只有\(1\)行,且都是\(1\),选择这行,问题就可以解决

故,问题的解就是矩阵\(A\)中的第一行、矩阵\(A_1\)中的第\(2\)行、矩阵\(A_3\)的第一行,即矩阵\(A\)中的第\(1、4、5\)

言归正传,DLX又是什么东西呢

介绍DL,舞蹈链

建出形如这样的交叉十字循环双向链只对有\(1\)的连边,这张图对应着矩阵\(A\)

获取\(head.right\)元素,即元素\(C1\),标示元素\(C1\)为紫色

先尝试行\(2\),标示该行中其他元素\(元素5和元素6\)所在的列首元素为橙色,即元素\(C4\)和元素\(C7\)

移除橙色部分和紫色部分,剩下的如图所示

获取\(head.right\)元素,即元素\(C2\),标示元素\(C2\)为紫色

\(C2\)只有元素\(7\)覆盖,故答案只能选择行\(3\)

选择行\(3\),同理,标示元素\(C3\)和标示元素\(C6\)为橙色

移除,得

没有元素能覆盖到\(C5\),说明求解失败,回溯

回标列首元素,其顺序是标示元素的顺序反过来,即回标列首C6、回标列首C3、回标列首C2、回标列首C7、回标列首C4

故不能选择行\(2\),尝试行\(4\),同理,标示元素\(C4\)为橙色

移除,得

获取\(head.right\)元素,标示元素\(C2\)为紫色

选择行\(3\),同理,标示元素\(C3\)\(C6\)为橙色

同理,得

同理,回溯,回标列首C6、回标列首C3

这次选择行\(5\),标示元素\(C7\)为橙色

移除,得

获取\(head.right\)元素,标示元素\(C3\)为紫色

只有元素\(1\)覆盖,故答案只能选择行\(3\),同理,标示元素\(C5\)和元素\(C6\)为橙色

移除,得

因为\(head.right=head\),故求解结束,答案栈中的答案分别是\(4、5、1\),表示该问题的解是第\(4、5、1\)行覆盖所有的列,即下图蓝色的部分

总结\(Dancing Links\)的求解

  • 函数入口
  • 判断$head.right \space ?= head $,若成立,输出答案,返回已解决,退出函数
  • 获得\(head.right\)的元素\(C\)
  • 标示元素\(C\)
  • 获得元素\(C\)所在列的一个元素
  • 标示该元素同行的其他元素所在的列首元素
  • 获得一个简化问题,递归,若返回已解决,则退出函数
  • 若刚刚尝试的不行,回标该元素同行的其他元素所在的列首元素,回标顺序与之前标示的顺序相反
  • 获得元素\(C\)所在列的下一个元素,若有,跳转“标示该元素同行的其他元素所在的列首元素”
  • 若没有,回标元素\(C\),返回未解决,退出函数

例题 P4929

代码实现

#include"bits/stdc++.h"
using namespace std;
const int N=250015;//N*M
#define inl inline
#define reg register
#define regi register int
#define PII pair<int,int>
inl int read(void)
{int x=0,f=1;char ch=getchar();while(!isdigit(ch))  f=ch=='-'?-1:f,ch=getchar();while(isdigit(ch))   x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;
}
int l[N],r[N],up[N],down[N],col[N],row[N];//每个点的左右上下指针,以及每个点所在的行列
int head[N];int sz[N];//每行的头结点,每列的节点数
int ans[N];//答案栈
int n,m,cnt;
void build(int m)
{for(int i=0;i<=m;i++)	r[i]=i+1,l[i]=i-1,up[i]=down[i]=i;r[m]=0,l[0]=m;memset(sz,0,sizeof sz);cnt=m+1;//已经处理了0行,从第一行开始insert
}
void insert(int R,int C)//在R行C列插入点
{sz[C]++;row[++cnt]=R,col[cnt]=C;up[cnt]=C,down[cnt]=down[C];up[down[C]]=cnt,down[C]=cnt;if(!head[R])	head[R]=r[cnt]=l[cnt]=cnt;else	r[cnt]=head[R],l[cnt]=l[head[R]],r[l[head[R]]]=cnt,l[head[R]]=cnt;
}
void remove(int C)//删除C列的集合
{r[l[C]]=r[C],l[r[C]]=l[C];for(int i=down[C];i!=C;i=down[i])for(int j=r[i];j!=i;j=r[j])up[down[j]]=up[j],down[up[j]]=down[j],sz[col[j]]--;
}
void resume(int C)//恢复C列的集合
{for(int i=up[C];i!=C;i=up[i])for(int j=l[i];j!=i;j=l[j])up[down[j]]=down[up[j]]=j,sz[col[j]]++;r[l[C]]=C,l[r[C]]=C;
}
bool dance(int dep)
{if(r[0]==0)//head.right=head{for(int i=0;i<dep;i++)	printf("%d ",ans[i]);return 1;}int C=r[0];for(int i=r[0];i;i=r[i])	if(sz[i]<sz[C])	C=i;//找到点最少的列(优化)remove(C);for(int i=down[C];i!=C;i=down[i]){ans[dep]=row[i];//压入答案栈for(int j=r[i];j!=i;j=r[j])	remove(col[j]);if(dance(dep+1))	return 1;for(int j=l[i];j!=i;j=l[j])	resume(col[j]);}resume(C);return 0;
}
int main(void)
{n=read(),m=read();int s;build(m);for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){s=read();if(s)	insert(i,j);}if(!dance(0))	puts("No Solution!");//无解return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/61942.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

java基础 -IO流笔记

610,文件的基础知识 文件流 输入流和输出流都是相对 java程序内存 而言611,创建文件 在D盘下创建文件。package com.hspedu.file;import org.junit.jupiter.api.Test; import java.io.File; import java.io.IOException;//演示创建文件 public class FileCreate {public sta…

2024软件工程个人作业(第二次)

这个作业属于哪个课程 https://edu.cnblogs.com/campus/fzu/SE2024这个作业要求在哪里 https://edu.cnblogs.com/campus/fzu/SE2024/homework/13253这个作业的目标 学会使用aigc编写学习代码,明白aigc能做哪些不能做哪些学号 1022014251. 项目展示 1.1. GitHub 仓库链接 ruang…

Kubernetes Ingress

目录一、为什么需要 Ingress二、什么是Ingress,Ingress Controller三、Ingress 的工作原理四、Ingress 配置资源模版五、实例1、搭建 Ingress 环境1.1、Ingress-Nginx官网地址1.2、master 节点下载 deploy.yaml1.3、所有节点提前 pull 必须的镜像1.4、修改并应用 deploy.yaml 文…

JVM--解析运行期优化与JIT编译器

JVM开发团队一直在努力,缩小Java与C/C++语言在运行效率上的差距。 本篇博客,我们来谈一谈JVM(HotSpot)为了提高Java程序的运行效率,都实现了哪些激动人心的技术~ 1 JIT编译器的引入 首先我们这篇文章中所说的编译器都是指JVM的组成部分之一---即时编译器(JIT),与生成J…

十七,Spring Boot 整合 MyBatis 的详细步骤(两种方式)

十七,Spring Boot 整合 MyBatis 的详细步骤(两种方式) @目录十七,Spring Boot 整合 MyBatis 的详细步骤(两种方式)1. Spring Boot 配置 MyBatis 的详细步骤2. 最后:MyBatis 的官方文档:https://mybatis.p2hp.com/ 关于 MyBatis 的学习的详细内容,大家可以移步至:✏️✏️…

学习高校课程-软件工程-软件流程(ch3)

3.1 A GENERIC PROCESS MODEL 通用过程模型 线性流和迭代流演化流和并行流3.2 DEFINING A FRAMEWORK ACTIVITY 定义框架活动 What actions are appropriate for a framework activity, given the nature of the problem to be solved, the characteristics of the people doing…