首页 > 代码库 > Dancing Links 学习 AND 代码详解
Dancing Links 学习 AND 代码详解
今天花时间学习了下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]
head就是头结点,cnt就是在构图时结点的编号,S[NN]是某一列上有多少个元素,COL[NN],ROW[NN]是一个点对应的行列数,H[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; } } }
上面知识Dancing Links 的几个基本操作还有涉及构图。下面以POJ 3740为例:
#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; }
还有一种就是原始的dfs:
#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; }
Dancing Links 学习 AND 代码详解
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。