跳舞链 学习笔记
djwj233
2020-10-02 20:38:12
## 跳舞链用来干什么
用来解决 `精准覆盖` 问题。
通常所说的跳舞链,其实是指 `DLX (Dancing Links 优化的 X 算法)`
## DLX讲解
[这篇](https://leverimmy.blog.luogu.org/dlx-xiang-xi-jiang-jie)讲得很好。
~~主要因为自己懒得写~~
## 模板
- [P4929 【模板】舞蹈链(DLX)](https://www.luogu.com.cn/problem/P4929)
```cpp
#include<bits/stdc++.h>
using namespace std;
#define fo(v,a,b) for(int v=a;v<=b;v++)
#define fo2(v,a,b) for(v=a;v<=b;v++)
#define fr(v,a,b) for(int v=a;v>=b;v--)
#define fe(v,a) for(int v=head[a];v;v=Next[v])
#define rg register
#define il inline
#define dlx(i,A,x) for(int i=A[x];i!=x;i=A[i])
const int RN=510,N=25010;
int n,m,a[RN][RN],cnt;
int L[N],R[N],U[N],D[N];
//这里的L,R,U,D都是循环的,即最后一个数的后一个就是第一个
int siz[N],fir[N],col[N],row[N];
//fir是每行的第一个元素,siz是每列的元素个数
//row--行 col--列
void build()//初始化一个Dancing Links,并未赋值
{
fo(i,0,m)
{
L[i]=i-1,R[i]=i+1;
U[i]=D[i]=i;
}
L[0]=m,R[m]=0;cnt=m;
}
void remove(int x)//删第x列
{
L[R[x]]=L[x],R[L[x]]=R[x];//先从链表中删第x列
dlx(i,D,x)//该列之下的有1的行
dlx(j,R,i)//该行中有1的地方
{
U[D[j]]=U[j],D[U[j]]=D[j];//删第j个数
siz[col[j]]--;//维护siz数组
}
}
void recover(int x)//恢复第x列,操作顺序恰与remove相反
{
dlx(i,U,x)
dlx(j,L,i)
{//同上
U[D[j]]=D[U[j]]=j;
siz[col[j]]++;
}
L[R[x]]=R[L[x]]=x;
}
void link(int r,int c)//新增一个(r,c)的节点,OI-Wiki中是insert
{
cnt++;//开始插入一个新节点cnt
col[cnt]=c,row[cnt]=r,siz[c]++;//维护基本信息
D[cnt]=D[c],U[D[c]]=cnt,U[cnt]=c,D[c]=cnt;//维护列信息
if(!fir[r])//该行没有元素
fir[r]=L[cnt]=R[cnt]=cnt;
else//在fir[r]之后插入
{
L[cnt]=fir[r],R[cnt]=R[fir[r]];
L[R[fir[r]]]=cnt,R[fir[r]]=cnt;
}
}
vector<int> ans;
bool dance(int now)
{
if(!R[0])return true;//当前Dancing Links为空,成功
int c=R[0];
dlx(i,R,0)
if(siz[i]<siz[c])
c=i;//取列元素个数最少的一列
remove(c);//将其删去
dlx(i,D,c)//遍历c一行的所有元素
{
ans.push_back(row[i]);//将当前行记录下来
dlx(j,R,i)remove(col[j]);
if(dance(now+1))return true;
dlx(j,L,i)recover(col[j]);
ans.pop_back();
}
recover(c);
return false;
}
int main()
{
cin>>n>>m;
build();
fo(i,1,n)
fo(j,1,m)
{
scanf("%d",&a[i][j]);
if(a[i][j])link(i,j);
}
if(dance(1))
fo(i,0,(int)ans.size()-1)
printf("%d ",ans[i]);
else puts("No Solution!");
return 0;
}
```