首页 > 其他分享 >DLX

DLX

时间:2023-10-06 21:46:10浏览次数:27  
标签:cnt 删除 int 链表 maxn 一列 DLX

写在前面

请和我起舞趁着故事还没有结束,天亮后让一切恢复。

用处

高效解决精确覆盖问题和完全覆盖问题。(诡异小搜索)

精确覆盖问题

定义

在全集\(S\)中有子集\(T_1,T_2,\dots , T_n\)从中选择若干个子集,使\(S\)中每一个元素在这些自给中分别恰好出现一次。

问题转化

给定一个\(n\)行\(m\)列的\(0,1\)矩阵(每行是一个集合,列代表每一个元素),选择其中几行,使每列恰好只有一个\(1\)。

DLX求解

直接大暴搜的时间复杂度是\(O(m2^n)\),妥妥的寄掉了\(\dots\)
所以\(\dots\)接着奏乐,接着舞!
具体过程:
递归删表,如果把表删完,代表每一列都被\(1\)精确覆盖,找到一组答案,如果没有删完,就要向下递归

  1. 找\(1\)的个数最少的列(\(1\)的个数越少,分支越少)。
  2. 把这一列的列表头删除(假的删除,假设这一列的列表头为\(x\),左边连接\(a\),右边连接\(b\),把\(a\)指向\(x\)的指针指向\(b\),\(b\)指向\(x\)的指针指向\(a\),但可以通过\(x\)的指针找到\(x\)左右的\(a和\)\(b\))。
  3. 把这一列的相关的行(这一列在哪一行有\(1\)就与哪一行相关)删除(这一列中各个指针不变,方便还原)。
  4. 记录下暂时选定哪一行。
  5. 把这一行相关的列(这一行在哪一列有\(1\)就与哪一行有\(1\)就与哪一行相关)删除,并一同删除被删除列相关的行。
  6. 继续向下递归。
  7. 如果这次选择不合法,恢复所删除的列和这一列相关的行。

为了完成灵活的删除和恢复操作,需要使用双向循环十字链表。在双向循环十字链表中每一个元素在横竖交点位置,每一个元素有四个指针,分别指向上下左右相邻的元素,链表左右边界相连,上下边界相连,完成循环。

举个形象的栗子

P4929
这是给定矩阵:

\[\begin{bmatrix} 0 & 0& 1& 1& 0& 0 \\ 1 & 0& 0& 0& 1& 1 \\ 0 & 1& 1& 0& 0& 0\\ 1 & 0& 0& 0& 1& 0\\ 0 & 1& 0& 0& 0& 1\\ 0& 1& 0& 1& 1& 1\end{bmatrix} \]

现在画出由以上矩阵构造的循环十字链表:
image
其中每个点带有\(6\)种属性:十字链表中的\(4\)个指针\(l,r,u,d\),所在的行\(row\),所在的列\(col\)。
在上图中有两种数字:

  1. 从\(7\)到\(21\)为给定矩阵中出现的\(1\)
  2. 从\(0\)到\(6\)为列表头,记录哪一列存在。

现在我们找到最左面\(1\)的个数最少的列为第\(1\)列,删去第\(1\)列,再删去与第\(1\)列相关的第\(2\),\(4\)行,与第\(2\)行相关的第\(5\),\(6\)列,与第\(5\),\(6\)列相关的第\(5\),\(6\)行,与第\(4\)行相关的第\(5\)列,和与第\(5\)列相关的行。
这是删除完一轮的样子:
image
之后再重复删除下一列,之后发现\(\dots\)寄了,第\(4\)列有剩余,所以恢复上次删除(选中)的行列,再选择其它的。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,m;
const int maxn=5510;
int u[maxn],d[maxn],l[maxn],r[maxn],cnt;
int l1[maxn],r1[maxn],h[maxn],s[maxn],ans[maxn];
void insert(int x,int y)
{
	l1[++cnt]=y; r1[cnt]=x; s[y]++;
	u[cnt]=u[y];  d[u[y]]=cnt; d[cnt]=y; u[y]=cnt;
	if(h[x]==0) h[x]=r[cnt]=l[cnt]=cnt;
	else
	{
		l[cnt]=l[h[x]]; r[l[h[x]]]=cnt; r[cnt]=h[x]; l[h[x]]=cnt;
	}
}
void del(int y)
{
	r[l[y]]=r[y]; l[r[y]]=l[y];
	for(int i=d[y];i!=y;i=d[i])
	{
		for(int j=r[i];j!=i;j=r[j])
		{
			u[d[j]]=u[j]; d[u[j]]=d[j]; s[l1[j]]--;
		}
	}
}
void resort(int y)
{
	r[l[y]]=y; l[r[y]]=y;
	for(int i=u[y];i!=y;i=u[i])
	{
		for(int j=l[i];j!=i;j=l[j])
		{
			u[d[j]]=j; d[u[j]]=j; s[l1[j]]++;
		}
	}
}
int dance(int a)
{
	if(r[0]==0)
	{
		for(int i=0;i<a;i++)
		{
			printf("%d ",ans[i]);
		}
		return 1;
	}
	int y=r[0];
	for(int i=r[0];i!=0;i=r[i])
	{
		if(s[i]<s[y]) y=i;
	}
	del(y);
	for(int i=d[y];i!=y;i=d[i])
	{
		ans[a]=r1[i];
		for(int j=r[i];j!=i;j=r[j])
		{
			del(l1[j]);
		}
		if(dance(a+1)) return 1;
		for(int j=l[i];j!=i;j=l[j])
		{
			resort(l1[j]);
		}
	}
	resort(y);
	return 0;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=0;i<=m;i++)
	{
		u[i]=d[i]=i; l[i]=i-1; r[i]=i+1;
	}
	l[0]=m; r[m]=0; cnt=m;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			int x; scanf("%d",&x);
			if(x) insert(i,j);
		}
	}
	if(!dance(0)) cout<<"No Solution!";
}

标签:cnt,删除,int,链表,maxn,一列,DLX
From: https://www.cnblogs.com/PHOTONwa/p/17744719.html

相关文章

  • 舞蹈链(DLX)
    简述舞蹈链用于解决精确覆盖问题精确覆盖问题给定许多集合\(S_i(1\lei\len)\)以及一个集合\(X\),求无序多元组\((T_1,T_2,\cdots,T_m)\)满足:\[\begin{alig......
  • 舞蹈链 (DLX, Dancing Links X) 算法笔记
    舞蹈链(DLX,DancingLinksX)算法精确覆盖问题在一个全集X中若干子集的集合为S,S的子集S*,满足X中的每一个元素在S*中恰好出现一次。通俗地讲,给定一个\(N\)行\(M\)......
  • 舞蹈链(DLX)
    精确覆盖问题の定义精确覆盖问题(英文:ExactCoverProblem)是指给定许多集合\(S_i(1\lei\len)\)以及一个集合\(X\),求满足以下条件的无序多元组\((T_1,T_2,\cdots......
  • weidlxDeepRec:热门微博推荐框架性能提升实战
    微博推荐团队:陈雨、韩楠、蔡小娟、高家华1.项目背景热门微博是新浪微博的重要功能之一,包含热门流、热点流、频道流、小视频后推荐、视频社区等场景。推荐首页发现页推荐沉......
  • RabbitMQ管理面板D,DLX的含义
    rabbitmq管理面板的Queues中的features各参数解释D:durable的缩写,代表这个队列中的消息支持持久化.AD:autoDelete的缩写,代表当前队列的最后一个消费者退订时被自动删......