版权声明:作为一个蒟蒻,转载时请通知我这个蒟蒻 https://blog.csdn.net/zyszlb2003/article/details/89318336
首先
舞蹈链(简称DLX,下文均用DLX表示舞蹈链),是一种数据结构,并不是算法,NOIP不经常考,但对于精确覆盖问题,例如LuoguP4929具有优秀的时间复杂度和愉快的AC感觉
关于具体的模拟过程,请参考跳跃的舞者,舞蹈链(Dancing Links)算法——求解精确覆盖问题
本文主要提供
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
using namespace std;
const int N=5510;
const int M=510;
int r[N],l[N],u[N],d[N],col[N],row[N],sz[M],head,num;
void remove(int p)
{
r[l[p]]=r[p];l[r[p]]=l[p];
for(int i=u[p];i!=p;i=u[i])
for(int j=r[i];j!=i;j=r[j])
u[d[j]]=u[j],d[u[j]]=d[j],--sz[col[j]];
}
void resume(int p)
{
r[l[p]]=l[r[p]]=p;
for(int i=u[p];i!=p;i=u[i])
for(int j=r[i];j!=i;j=r[j])
u[d[j]]=d[u[j]]=j,++sz[col[j]];
}
void link(int i,int j)
{
++num;
u[num]=u[j];d[num]=j;col[num]=j;row[num]=i;
u[d[num]]=d[u[num]]=num;++sz[j];
if(!head)head=num;
else l[num]=num-1,r[num-1]=num;
}
bool bk;
void dance()
{
if(!r[0]){bk=true;return ;}
int p=r[0];
for(int i=r[p];i;i=r[i])if(sz[i]<sz[p])p=i;
remove(p);
for(int i=d[p];i!=p;i=d[i])
{
for(int j=r[i];j!=i;j=r[j])remove(col[j]);
dance();if(bk){printf("%d ",row[i]);return ;}
for(int j=r[i];j!=i;j=r[j])resume(col[j]);
}
resume(p);
}
int main()
{
int n,m;scanf("%d%d",&n,&m);
for(int i=0;i<=m;i++)l[i]=i-1,r[i]=i+1,u[i]=d[i]=i;
l[0]=m;r[m]=0;num=m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
int x;scanf("%d",&x);
if(x)link(i,j);
}
if(head)l[head]=num,r[num]=head,head=0;
}
dance();
if(!bk)puts("No Solution!");
return 0;
}
2.就模版中一些数组的理解
DLX实际上是一个双向十字链表。
r[N],l[N]:
0~n
实际上编译时会改变的范围,也就是上文提到的链接中的列标C1,C2,......,Cn,之后要形成一个
闭间,即为0~n.
int p=r[0];
for(int i=r[p];i;i=r[i])if(sz[i]<sz[p])p=i;这里即用到的是r[0~n]区间的
r[l[p]]=r[p];l[r[p]]=l[p];暂时隐去p这一列(以后可能会回溯,就是下面那一行)
r[l[p]]=l[r[p]]=p;
n+1~num
实际上指的是某一行中的‘1’的位置,每一行都会形成一个闭区间,即
if(head)l[head]=num,r[num]=head,head=0;
u[N],d[N]:
0~n
分别指向列表自身的数值,也是引出具体‘1’的闭区间开头,不会改变。
n+1~num
形成一个列的链表,自身处在j列,即由u[j]引出链表。
模拟列的链表如何形成
;插入
,即构成一个
这样一个闭区间.
再插入
形成
这样一个闭区间
以此类推即可。
下来就是找size最小的列(因为这样可以使状态变少),
然后找到一个‘1’的位置,标记这一行,
找出被标记‘1’的位置,后将被标记这一列,
标记这一列中‘1’所处的位置,隐去‘1’所在行的所有‘1‘的标识。
再执行一次dance
如果不行,则回溯。
3.对于实际应用的难点
DLX主要难在于如何构图,以及如何记录答案
因为很少会有裸的精确覆盖或者裸的重复覆盖。
因此推荐几道题,用DLX做
数独三连POJ2676、POJ3074、POJ3076、题解传送门
靶形数独 NOIP提高组第四题、题解传送门