标签:dangcing links 搜索 优化 链表
今天花时间学习了下Dancing Links,其核心思想是降低在搜索中的范围,减少复杂。降低的方法就是将用链式结构构造的图中不需要的点去掉。如果回溯再恢复。
这个方法依赖的数据结构是用数组存储的十字链表L[NN],R[NN],U[NN],D[NN] 左右上下的链接

构造数据结构:
head,cnt,L[NN],R[NN],U[NN],D[NN],H[NN],COL[NN],S[NN],ROW[NN]
主要的数据结构就是这样。接下来看代码
#define head 100
int U[N],D[N],L[N],R[N];
int C[N],H[N],ans[N];//C[N]表示N的列标,H[N]表示N的行标,ans[]用来储存结果
bool dance(int k)
{
int c = R[head];
if(c==head) {
Output();//Output the solution
return true;
}
remove(c);
for(int i=D[c]; i!=c; i=D[i])
{
ans[k] = H[i];
for(int j=R[i]; j!=i; j=R[j]) remove(C[j]);
if(dance(k+1)) return true;
for(int j=L[i]; j!=i; j=L[j]) resume(C[j]);//这里原因和resume中的一样
}
resume(c);
return false;
}
void remove(int c)
{
L[R[c]] = L[c];
R[L[c]] = R[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];
}
}
}
/*
resume还有另一种写法,就是将L,R的改变放在最后,循环中U,D调换位置。这其实没什么影响因为L,R都是一条语句不影响其他
U,D都是单个操作,而外层循环必须是U的循环,即一个个拿下来在按顺序放回去,不然会错误,自己用纸试一下
*/
void resume(int c)
{
L[R[c]] = c;
R[L[c]] = c;
for(int i=U[c]; i!=c; i=U[i]) {
for(int j=R[i]; j!=i; j=R[j]) {
U[D[j]] = j;
D[U[j]] = j;
}
}
}
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int NN=600;
int head,cnt,L[NN],R[NN],U[NN],D[NN],H[NN],C[NN],S[NN],ROW[NN];
//上下左右 一行的最右,C结点对应的列,S该列的个数
int N,M;
/*
这里插入一个元素,则对竖直方向改变不大 原本的上一个元素的D,表头的U,当前的D指向表头,U指向表头暂存的
对于水平方向,注意开头的那个元素的L,前一个元素的R,自己的L=前一个,R=开头。开头标号的暂存在前一个的R中
*/
void insert(int i,int j)
{
C[++cnt]=j;
S[j]++;
D[cnt]=j;
U[cnt]=U[j];
if(H[i])
{
R[cnt]=R[H[i]];
L[cnt]=H[i];
R[H[i]]=cnt;
L[R[cnt]]=cnt;
}
else R[cnt]=L[cnt]=cnt;
D[U[j]]=cnt;
U[j]=cnt;
H[i]=cnt;
}
void remove(int c)//删除第c列上的元素所在的行
{
R[L[c]]=R[c];
L[R[c]]=L[c];
//删除c,c是列指针,删除了c就代表删除了整列,因为递归后不可能访问到c了
for (int i=D[c]; i!=c; i=D[i]) //c所在列上的元素i
for (int j=R[i]; j!=i; j=R[j]) //i和j一行的,删掉j
{
U[D[j]]=U[j];
D[U[j]]=D[j];
S[C[j]]--;//j所在列的元素(‘1’的个数)-1;
}
}
void resume(int c)//对应的恢复操作
{
L[R[c]] = c;
R[L[c]] = c;
for(int i=U[c]; i!=c; i=U[i]) {
for(int j=R[i]; j!=i; j=R[j]) {
U[D[j]] = j;
D[U[j]] = j;
}
}
}
bool dance()
{
int i,j;
if (R[head]==head)//列指针删完了,任务完成
{
puts("Yes, I found it");
return true;
}
int s=0x3fff,c;
for ( i=R[head]; i!=head; i=R[i]) if (S[i]<s) s=S[i],c=i;
//找元素最少的列c,一种优化
remove(c);//删除列c
for ( i=D[c]; i!=c; i=D[i])
{
for ( j=R[i]; j!=i; j=R[j]) remove(C[j]);//删除所有可能的冲突元素
if (dance()) return true;
for ( j=L[i]; j!=i; j=L[j]) resume(C[j]);
}
resume(c);
return false;
}
/*
初始化注意,由于有head,则构图时下标都从1开始,每个元素都是自然下标
cnt的赋值不要忘记
*/
void init()
{
int i,j,k;
for(i=0;i<=M;i++)
{
L[i+1]=i;
R[i]=i+1;
U[i]=D[i]=C[i]=i;
S[i]=0;
}
L[0]=M,R[M]=0;
cnt=M;
for(i=1;i<=N;i++)
{
H[i]=0;
for(j=1;j<=M;j++)
{
scanf("%d",&k);
if(k)
insert(i,j);
}
}
}
int main()
{
int c,i;
head=0;
while (~scanf("%d%d",&N,&M))
{
init();
if(!dance())
puts("It is impossible");
}
return 0;
}
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <math.h>
using namespace std;
int N,M,T;
int maps[20][310];
int rows[310];
int dfs(int n,int cnt)
{
if(cnt==N) return 1;
int i,j,k;
for(i=n;i<M;i++)
{
k=cnt;
for(j=0;j<N;j++)
if(maps[i][j]==1 && rows[j]==1)
break;
if(j<N)
continue;
for(j=0;j<N;j++)
if(maps[i][j]==1)
rows[j]=1,k++;
if(dfs(i+1,k))
return 1;
for(j=0;j<N;j++)
if(maps[i][j]==1)
rows[j]=0;
}
return 0;
}
int main(){
while(scanf("%d%d",&M,&N)!=EOF)
{
int i,j;
memset(rows,0,sizeof(rows));
memset(maps,0,sizeof(maps));
for(i=0;i<M;i++)
for(j=0;j<N;j++)
scanf("%d",&maps[i][j]);
if(dfs(0,0))
printf("Yes, I found it\n");
else
printf("It is impossible\n");
}
return 0;
}标签:dangcing links 搜索 优化 链表
原文地址:http://blog.csdn.net/gg_gogoing/article/details/40038909