Dancing Links X
应用:解决精确覆盖问题。
例如数独,k皇后等涉及同一个数在一定范围内值出现一次的问题可以用DLX解决。
模板
P4929
- 注意:
1.每次删除和恢复的都是表头下面的一整列以及含有1的一整行,而整个过程中更改的只有表头左右指针和不是表头那列的1的上下指针
(因为恢复要从表头向下访问,再从行头向右访问)。
只有这样才能实现访问到已删除的1。
2.十字链表中上下,左右的顺序无所谓,但必须统一。
3.注意所有链表都是双向,即是一个环。当自己指向自己时为空。
4.算法判定结果错误的依据是表头还有剩余,但元素都删完了,说明之前有两个以上的1冲突。
5.删除时更改上下元素的之中呢一定注意顺序。
f[++cnt].u=C;
f[cnt].d=f[C].d;
f[f[C].d].u=cnt;
f[C].d=cnt;
f[cnt].row=R;
f[cnt].col=C;
6.每次选用1个数最小的列实际也是剪枝优化,为了让更多的1在当前回合就被筛掉,以减少搜索次数。
7.去除元素时是去除除了表头元素意外所有下面的1,和除了行头外所有1.为了下次从表头,行头访问到被删除的点。 code time:
1) 数组版:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=550,M=N*N+N;
int n,m,cnt;
int d[M],u[M],l[M],r[M],row[M],col[M];
int s[M],h[M],ans[M];
void init(){
for(int i=0;i<=m;++i){
l[i]=i-1;
r[i]=i+1;
u[i]=d[i]=i;
}
r[m]=0;
l[0]=m;//链表头尾特判
memset(h,-1,sizeof h);
memset(s,0,sizeof s);
cnt=m+1;
}
void add(int R,int C){
s[C]++;
row[cnt]=R;
col[cnt]=C;
u[cnt]=C;
d[cnt]=d[C];
u[d[C]]=cnt;
d[C]=cnt;//注意顺序!
if(h[R]==-1){
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;//顺序
}
cnt++;
}
void remove(int 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]]--;
}
}
}
void resume(int 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]]++;
}
}
l[r[C]]=C,r[l[C]]=C;
}
void print1(int tot){
for(int i=0;i<tot;++i)printf("%d ",ans[i]);
}
int dfs(int dep){
if(r[0]==0){
print1(dep);
return 1;//一个解就退出
}
int c=r[0];
for(int i=r[0];i!=0;i=r[i]){
if(s[i]<s[c])c=i;
}//剪枝优化
remove(c);//去除当前表头下那列(C')的所有1,表示已经被纳入选择。
for(int i=d[c];i!=c;i=d[i]){
ans[dep]=row[i];//选择当前行。
for(int j=r[i];j!=i;j=r[j])remove(col[j]);//去除除了C'以外的当前行的1
if(dfs(dep+1))return 1;
for(int j=r[i];j!=i;j=r[j])resume(col[j]);
}
resume(c);//回溯
return 0;//无解
}
int read1(){
int x=0;
char ch=getchar();
while(ch>'9' || ch<'0'){
ch=getchar();
}
while(ch<='9' && ch>='0'){
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x;
}
int main(){
n=read1(),m=read1();
init();
for(int i=1;i<=n;++i){
for(int j=1,tmp;j<=m;++j){
tmp=read1();
if(tmp)add(i,j);
}
}
if(!dfs(0))printf("No Solution!");
return 0;
}
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=550,M=N*N;
int n,m,cnt;
int s[M],h[M],ans[M];
struct node{
int u,d,l,r,row,col;
}f[M];
void init(){
for(int i=0;i<=m;++i){
f[i].l=i-1;
f[i].r=i+1;
f[i].u=f[i].d=i;
}
f[m].r=0;
f[0].l=m;
cnt=m;
memset(h,-1,sizeof h);
memset(s,0,sizeof s);
}
void link(int R,int C){
s[C]++;
f[++cnt].u=C;
f[cnt].d=f[C].d;
f[f[C].d].u=cnt;
f[C].d=cnt;
f[cnt].row=R;
f[cnt].col=C;
if(h[R]==-1){
h[R]=f[cnt].l=f[cnt].r=cnt;
}else{
f[cnt].l=h[R];
f[cnt].r=f[h[R]].r;
f[f[h[R]].r].l=cnt;
f[h[R]].r=cnt;
}
}
void remove(int C){
f[f[C].r].l=f[C].l,f[f[C].l].r=f[C].r;
for(int i=f[C].d;i!=C;i=f[i].d){
for(int j=f[i].r;j!=i;j=f[j].r){
f[f[j].d].u=f[j].u;
f[f[j].u].d=f[j].d;
s[f[j].col]--;
}
}
}
void resume(int C){
for(int i=f[C].d;i!=C;i=f[i].d){
for(int j=f[i].r;j!=i;j=f[j].r){
f[f[j].d].u=j;
f[f[j].u].d=j;
s[f[j].col]++;
}
}
f[f[C].l].r=C,f[f[C].r].l=C;
}
void print1(int tot){
for(int i=1;i<=tot;++i)printf("%d ",ans[i]);
}
int dfs(int dep){
if(f[0].r==0){
print1(dep-1);
return 1;
}
int c=f[0].r;
for(int i=f[0].r;i!=0;i=f[i].r)if(s[i]<s[c])c=i;
remove(c);
for(int i=f[c].d;i!=c;i=f[i].d){
ans[dep]=f[i].row;
for(int j=f[i].r;j!=i;j=f[j].r)remove(f[j].col);
if(dfs(dep+1))return 1;
for(int j=f[i].r;j!=i;j=f[j].r)resume(f[j].col);
}
resume(c);
return 0;
}
int main(){
scanf("%d%d",&n,&m);
init();
for(int i=1;i<=n;++i){
for(int j=1,tmp;j<=m;++j){
scanf("%d",&tmp);
if(tmp)link(i,j);
}
}
if(!dfs(1))printf("No Solution!");
return 0;
}