本文主要是介绍Vision_数据结构_舞蹈连,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
///定义(ctrl+c):
/*
(1)历史:
X算法是高德纳提出的解决精确覆盖问题的算法,而dancing links X算法则是DonKnuth(《计算机程序设计艺术》
的作者)提出的对X算法的一种高效实现,这种实现建立在如上所说的矩阵表示法上。
(2)算法思想:
由如上精确覆盖问题的矩阵表示法中,我们知道dancing links x 是用来求解一个 01矩阵中选取哪几行可以使得
这几行每一列都有且仅有一个1(就是每个元素在这几个子集里有且仅有出现过一次)。先不管他的实际意义,
我们需要做的就是在一个01矩阵的选取某几行使之符合上述条件。我们很容易就想到枚举,然后判断符不符合条件,
但是这个做法实在是太消耗时间。dacing links x就是一个高效的求解该类问题的算法,而这种算法,基于交叉十字
循环双向链的数据结构。
(3)精确覆盖问题的定义:
给定一个由0-1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1
(4)重复覆盖问题:
即选取一个01矩阵中的几行,使这几行组成的新矩阵的每一列至少有一个1。
该问题在精确覆盖问题上减少了一个约束条件
(5)应用:
解决精确覆盖问题和重复覆盖问题
*/
///代码:
/*
**name:舞蹈连-精确覆盖问题
**function:给定一个由0-1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1
**输入参数:包含1的位置(i,j)
**输出参数:行集合的信息
*/
#include <iostream>
#include <cstdio>
using namespace std;const int maxnode = 100010;
const int maxm = 1010;
const int maxn = 1010;struct DLX{int n,m,len;int U[maxnode],D[maxnode],R[maxnode],L[maxnode],Row[maxnode],Col[maxnode];int H[maxn];//行头结点int S[maxm];//每列有多少个结点int ansd,ans[maxn];//如果有答案,则选了ansd行,具体是哪几行放在ans[]数组里面,ans[0~ansd-1]void init(int _n,int _m){n = _n;m = _m;for(int i = 0; i <= m; i++){S[i] = 0;U[i] = D[i] = i;//初始状态时,上下都指向自己L[i] = i-1;R[i] = i+1;}R[m] = 0,L[0] = m;len = m;//编号,每列都有一个头结点,编号1~mfor(int i = 1; i <= n; i++)H[i] = -1;//每一行的头结点}void link(int r,int c){//第r行,第c列++S[Col[++len]=c];//第len个节点所在的列为c,当前列的结点数++Row[len] = r;//第len个结点行位置为rD[len] = D[c];U[D[c]] = len;U[len] = c;D[c] = len;if(H[r] < 0)H[r] = L[len] = R[len] = len;else{R[len] = R[H[r]];L[R[H[r]]] = len;L[len] = H[r];R[H[r]] = len;}}void del(int c){//删除结点c,以及c上下结点所在的行//每次调用这个函数,都是从列头节点开始向下删除,这里c也可以理解为第c列//因为第c列的列头节点编号为cL[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];--S[Col[j]];}}}void resume(int c){//回复结点c,以及c上下节点所在的行(同上,也可以理解为从第c列的头节点开始恢复for(int i = U[c]; i != c; i = U[i]){for(int j = L[i]; j != i; j = L[j]){++S[Col[U[D[j]]=D[U[j]]=j]];}}L[R[c]] = R[L[c]] = c;}bool dance(int d){//递归深度if(R[0] == 0){ansd = d;return true;}int c = R[0];for(int i = R[0]; i != 0; i = R[i]){if(S[i] < S[c])c = i;}del(c);//找到结点数最少的列,当前元素不是原图上0,1的节点,而是列头节点for(int i = D[c]; i != c; i = D[i]){ans[d] = Row[i];//列头节点下面的一个节点for(int j = R[i]; j != i; j = R[j])del(Col[j]);if(dance(d+1))//找到,返回return true;for(int j = L[i]; j != i; j = L[j])resume(Col[j]);}resume(c);return false;}
};DLX head;int main(){int n,m,k;while(~scanf("%d%d%d",&n,&m,&k)){head.init(n,m);for(int i = 1,x; i <= n; i++){for(int j = 1;j<=m;j++){scanf("%d",&x);if(!x)head.link(i,j);}}if(!head.dance(0))printf("NO\n");else{printf("%d\n",head.ansd);for(int i = 0; i < head.ansd; i++)printf(" %d",head.ans[i]);printf("\n");}}return 0;
}
/*
**name: 舞蹈连-重复覆盖问题
**function: 即选取一个01矩阵中的几行,使这几行组成的新矩阵的每一列至少有一个1。
**输入参数:包含1的位置(i,j)
**输出参数:至少所消耗的行数,如果返回无穷大则表明不存在
*/
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
struct DLX
{int n,m,len,ansd;int S[400],U[200000],D[200000],L[200000],R[200000],Row[200000],Col[200000];int H[400];void init(int _n,int _m){n=_n;m=_m;len=m;ansd=0x3f3f3f3f;for(int i=0; i<=m; i++){S[i]=0;U[i]=D[i]=i;R[i]=i+1;L[i]=i-1;Row[i]=0;Col[i]=i;}R[m]=0;L[0]=m;for(int i=1; i<=n; i++)H[i]=-1;}void link(int r,int c){++len;++S[c];Row[len]=r;Col[len]=c;D[len]=D[c];U[D[c]]=len;U[len]=c;D[c]=len;if(H[r]==-1){H[r]=R[len]=L[len]=len;}else{R[len]=R[H[r]];L[R[H[r]]]=len;L[len]=H[r];R[H[r]]=len;}}int f(){int ans=0;int V[400];memset(V,0,sizeof(V));for(int i=R[0]; i!=0; i=R[i]){V[i]=true;}for(int i=R[0]; i!=0; i=R[i]){if(V[i]){ans++;V[i]=false;for(int j=D[i]; j!=i; j=D[j]){for(int k=R[j]; k!=j; k=R[k]){V[Col[k]]=false;}}}}return ans;}void del(int c){for(int i=D[c]; i!=c; i=D[i]){R[L[i]]=R[i];L[R[i]]=L[i];}}void resume(int c){for(int i=U[c]; i!=c; i=U[i]){L[R[i]]=i;R[L[i]]=i;}}void Dance(int d){if(d>=ansd)return ;if(d+f()>=ansd)return ;if(R[0]==0){ansd=min(ansd,d);return ;}int c=R[0];for(int i=R[0]; i!=0; i=R[i]){if(S[i]<S[c])c=i;}for(int i=D[c]; i!=c; i=D[i]){del(i);for(int j=R[i]; j!=i; j=R[j])del(j);Dance(d+1);for(int j=L[i]; j!=i; j=L[j])resume(j);resume(i);}return ;}
};
DLX head;
int main(){int n,m,k,x;while(~scanf("%d%d%d",&n,&m,&k)){head.init(n,m);for(int i = 1;i<=n;i++){for(int j = 1;j<=m;j++){scanf("%d",&x);if(!x)head.link(i,j);}}head.Dance(0);if(head.ansd<=k&&head.ansd!=0x3f3f3f3f)printf("yes\n");else printf("no\n");//printf("%d\n",head.ansd);}return 0;
}
/*
**name:舞蹈连-精确覆盖问题
**function:求解数独
**输入参数:数独盘的字符串
**输出参数:完整的数独盘字符串
*/
#include <cstring>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <queue>
#include <cmath>
#include <algorithm>using namespace std;
const int maxn=1000,maxm=400,maxnode=400000;
struct DLX
{int n,m,len;int U[maxnode],D[maxnode],R[maxnode],L[maxnode],Row[maxnode],Col[maxnode];int H[maxn];///行头结点int S[maxm];///每列有多少个结点int ansd,ans[maxn];void init(int _n,int _m){n=_n;m=_m;for(int i=0; i<=m; i++){S[i]=0;U[i]=D[i]=i;L[i]=i-1;R[i]=i+1;}R[m]=0,L[0]=m;len=m;for(int i=1; i<=n; i++){//cout<<"**"<<endl;H[i]=-1;}}void link(int r,int c){++S[Col[++len]=c];Row[len]=r;D[len]=D[c];U[D[c]]=len;U[len]=c;D[c]=len;if(H[r]<0)H[r]=L[len]=R[len]=len;else{R[len]=R[H[r]];L[R[H[r]]]=len;L[len]=H[r];R[H[r]]=len;}}void del(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];--S[Col[j]];}}}void resume(int c){for(int i=U[c]; i!=c; i=U[i]){for(int j=L[i]; j!=i; j=L[j]){++S[Col[U[D[j]]=D[U[j]]=j]];}}L[R[c]]=R[L[c]]=c;}bool dance(int d){//cout<<d<<endl;if(R[0]==0){ansd=d;return true;}int c=R[0];///找一个1少的列,相当于剪枝了for(int i=R[0]; i!=0; i=R[i]){//cout<<S[i]<<endl;if(S[i]<S[c]){c=i;}}del(c);//cout<<S[1]<<endl;//cout<<"del()"<<c<<endl;//cout<<D[c]<<" "<<endl;for(int i=D[c]; i!=c; i=D[i]){//cout<<i<<endl;ans[d]=Row[i];for(int j=R[i]; j!=i; j=R[j]){del(Col[j]);}if(dance(d+1))return true;for(int j=L[i]; j!=i; j=L[j]){resume(Col[j]);}}resume(c);return false;}
};
struct node
{int pos;int data;int lie;
} q[2000];
DLX head;
int main()
{char s[100];while(scanf("%s",s)){if(strcmp(s,"end")==0)break;int len=strlen(s);head.init(1000,324);int row=0;for(int i=0; i<len; i++){int x=i/9,y=i%9;if(s[i]=='.'){for(int j=1; j<=9; j++){//x行y列填了一个数字head.link(++row,x*9+(y+1));//x行填了一个数字jhead.link(row,x*9+j+81);//y列填了一个数字jhead.link(row,y*9+j+162);//g宫填了一个数字jint xx=x/3*3,yy=y/3*3;int g=xx/3*3+yy/3+1;head.link(row,(g-1)*9+j+243);q[row].pos=i;q[row].data=j;q[row].lie=x*9+y+1;}}else{int a=s[i]-'0';//x行y列填了一个数字head.link(++row,x*9+(y+1));//x行填了一个数字jhead.link(row,x*9+a+81);//y列填了一个数字jhead.link(row,y*9+a+162);//g宫填了一个数字jint xx=x/3*3,yy=y/3*3;int g=xx/3*3+yy/3+1;head.link(row,(g-1)*9+a+243);q[row].pos=i;q[row].data=a;q[row].lie=x*9+y+1;}}//cout<<head.S[1]<<endl;bool ok=head.dance(0);//cout<<ok<<endl;//cout<<head.ansd<<endl;for(int i=0; i<head.ansd; i++){if(s[q[head.ans[i]].pos]=='.')s[q[head.ans[i]].pos]='0'+q[head.ans[i]].data;}//cout<<endl;printf("%s\n",s);}return 0;
}
///扩展:
/*
舞蹈连的步骤:
1、从矩阵中选择一行
2、根据定义,标示矩阵中其他行的元素
3、删除相关行和列的元素,得到新矩阵
4、如果新矩阵是空矩阵,并且之前的一行都是1,那么求解结束,跳转到6;新矩阵不是空矩阵,
继续求解,跳转到1;新矩阵是空矩阵,之前的一行中有0,跳转到5
5、说明之前的选择有误,回溯到之前的一个矩阵,跳转到1;如果没有矩阵可以回溯,说明该问题无解,跳转到7
6、求解结束,把结果输出
7、求解结束,输出无解消息
*/
这篇关于Vision_数据结构_舞蹈连的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!