kuangbin专题三DancingLink【从入门到熟练】【9+1道题】
【ZOJ 3209 Treasure Map】
由于exact cover模板题提交不了,舞蹈链a的第一题就是这个了。
这需要一点简单建模,但我还是先说一下模板
大家都直接用的板子,我觉得这样没什么问题,但还是自己学了一遍,手写了一遍,挺巧的是发现了一个问题。
这个是kuangbin的模板,想必大家大多数用的也是这个。但我看的时候是实在没能理解是什么意思,因为我对于link时候的理解是这样的:
然后我就一直一直想,发现把kuangbin代码里的L数组换成R,R数组换成L数组就能理解了!
也就是说kuangbin的模板是反着写的。。。感觉还比较坑人
我学的时候没有找到视频讲解,就看了这个博客:https://www.cnblogs.com/grenet/p/3145800.html
写的真的很好,估计以后做数独题还得看他写的博客。不过我看的时候有一个问题,就是不明白为什么要按照【删除】的相反顺序来【恢复】。(很容易理解为什么这么做是对的,因为就是把过程逆一下,但不理解为什么一定要这样做)其实不一定要这么做,但这么做更好。如果我们按照删除的先后顺序来恢复的话,在这道题里会TLE!因为每一次恢复都要在这一列一直down下去,在每一行都要right遍历一遍;而如果先恢复了先删除的,那会出现【重复恢复行的情况】!
具体点来说就是我们要删很多列,当删掉列col的时候,col往下遍历一遍访问的结点已经不是拿最开始的col访问得到的了。因为在删除col之前还删除了很多其他col,新的col是所有其他列的删除对其造成影响后的结果。【所以新的col会被原来的小很多】这个时候我们再考虑按照删除的顺序恢复,那等到再恢复到col的时候,这个col已经又是完整的了(因为在他之前删除的列都恢复了)。考虑到resume的实现过程,恢复一个完整的列肯定比恢复一个较短的列要慢很多,所以会重复恢复行!
而反着恢复就避免了这一问题。看上面博客的样例就能想清楚这一问题。
最后讲一下建模思路,
每个piece是一行,地图上的每一块1*1的面积是一列(不能说一个点是一列,因为我们覆盖的是面积),
最后要想的是给矩阵(x1,y1) (x2,y2)要怎么对应上它覆盖的面积(也就是每一列)。
画一画就能想到把x1,y1都加1,然后双重循环就可以了。【因为每个在矩形里的点,都能对应着其左下方的那块面积(除了在下边和左边上的点)】
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<map> #include<algorithm> #include<vector> #define INF 2e9 #define maxnode 500010 #define maxM 1010 #define maxN 510 using namespace std; struct DLX{ int n,m,id; int up[maxnode],down[maxnode],L[maxnode],R[maxnode]; int row[maxnode],col[maxnode];//返回该node的行和列 int h[maxN],s[maxM];//h[i]是第i行的第一个1的index,s[i]是第i列有1的数量 int ansd;//,ans[MAXN]; void init(int n1,int m1){ n=n1; m=m1; for(int i=0;i<=m;i++){//初始化【列标元素】和头元素,id为1-m,head是0 s[i]=0;//第i列有多少个1 L[i] = i-1; R[i] = i+1; up[i] = down[i] = i;//上下循环 } R[m]=0; L[0]=m; id=m;//m以后的都是普通结点 for(int i=1;i<=n;i++) h[i]=-1; } void link(int r,int c){//第i行第j列有一个1 s[c]++; row[++id] = r; col[id] = c; up[id] = up[c];//列标元素的up就是这一行的最后一个1 down[id]=c; down[ up[c] ]=id; up[c]=id; if( h[r]==-1 ) h[r]=L[id]=R[id]=id;//是这一行的第一个1 else{ L[id]= L[ h[r] ]; R[id]=h[r]; R[ L[h[r]] ]=id; L[ h[r] ]=id; } } void remove(int c){//删除这一行 R[L[c]]=R[c]; L[R[c]]=L[c]; for(int i=down[c];i!=c;i=down[i]){ for(int j=R[i];j!=i;j=R[j]){ up[ down[j] ] = up[j]; down[ up[j] ] = down[j]; s[ col[j] ]--; } } } void resume(int c){ R[L[c]]=c; L[R[c]]=c; for(int i=down[c];i!=c;i=down[i]){ for(int j=R[i];j!=i;j=R[j]){ down[up[j]]=j; up[down[j]]=j; s[col[j]]++; } } } void dance(int d){//d为深度 if( ansd!=-1 && d>=ansd) return; //最优性剪枝 if(R[0]==0){//第0行没有结点,被成功覆盖 if(ansd == -1)ansd = d; else if(d < ansd)ansd = d; return; } int c=R[0]; for(int i = R[0];i != 0;i = R[i]) if(s[i] < s[c]) c = i; remove(c); for(int i=down[c];i != c;i = down[i]){//选择用第i行的1来cover第c列所需要的1 for(int j=R[i];j != i;j = R[j]) remove( col[j] ); dance(d+1); for(int j=L[i];j!=i;j=L[j]) resume( col[j] ); } resume(c); } }dlx; int main(){ ios::sync_with_stdio(false); int T; int n,m,p; scanf("%d",&T); while(T--) { scanf("%d%d%d",&n,&m,&p); dlx.init(p,n*m); int x1,y1,x2,y2; for(int k = 1;k <= p;k++) { scanf("%d%d%d%d",&x1,&y1,&x2,&y2); for(int i = x1+1;i <= x2;i++) for(int j = y1+1;j <= y2;j++) dlx.link(k,j + (i-1)*m); } dlx.ansd = -1; dlx.dance(0); printf("%d\n",dlx.ansd); } return 0; }
========================================================================================================================================
【HDU 2295 Radar】
debug了好久。。
最后终于找到了错误在于给row和col的空间应该是maxnode,而我给的是maxN和maxM。orz。所以舞蹈链的题有个要注意的点是【空间!】
建模比较简单,因为比较套路,想一下列和行分别是什么就能做出来了。注意精度得是1e-8才能过。
注意还要剪一下枝,就是d+f()<=k,f()返回填满剩下列的至少行数,这是优于d<=k的可行性剪枝(因为能剪掉更多东西)。
怎么设计f()呢,right[0]这一列必须要满足,但我们不知道选哪一行最好,所以我们就把所有行都选上(因为最好的也不好于都选上),再把对应的列数消一下。然后再right看下一列,这样得到的cnt就是至少的行数。这个行数一定小于等于可行的最小需要行数。
最后简单说一下可重复覆盖的模板怎么写:
比不可重复的模板好写。区别在于remove和resume上。我们只需要删除这一列就可以了(因为可重复覆盖)
【实现的时候还是比较精妙的,我觉得比精确覆盖的代码要难想】
remove(i)是把i这一列上的所有元素(除了i结点外),斩断左右链。基于此,在dance的时候,由于i的左右链被保留下来了,所以可以R[i]下去删除这一行的其他元素所在列。
试想一下,如果我们按照精确覆盖的写法去写会出现什么解决不了的问题,
如果我们在循环前remove(c)的话,那会发现c列元素以下的所有元素的左右链都被斩断了,我们没有办法枚举每一行然后再靠R遍历这一行上的所有结点!
那为什么精确覆盖的时候不会出现这个问题呢?
因为remove方式不一样,精确覆盖里只会删除上下链,而重复覆盖是删除左右链。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<map> #include<iomanip> #include<algorithm> #include<vector> #define INF 2e9 #define maxnode 3000 #define maxM 55 #define maxN 55 using namespace std; double city1[55],city2[55],radar1[55],radar2[55]; double dist[100][100]; const double eps = 1e-8; struct DLX{ int n,m,k;//k为限制搜索深度 int up[maxnode],down[maxnode],left[maxnode],right[maxnode]; int h[maxN],s[maxM],row[maxnode],col[maxnode]; int id,ansd,ans[maxN]; void init(int n1,int m1,int k1){ n=n1; m=m1; k=k1; ansd=0;//无解 for(int i=0;i<=m;i++){ s[i]=0; left[i]=i-1; right[i]=i+1; up[i] = down[i] = i; } left[0]=m; right[m]=0; id=m; for(int i=1;i<=n;i++) h[i]=-1; } void link(int r,int c){ s[c]++; row[ ++id ] = r; col[id]=c; down[ up[c] ] = id; up[id]= up[c]; up[c] = id; down[id]=c; if( h[r]==-1 ) h[r]=left[id]=right[id]=id; else{ left[id]=left[h[r]]; right[id]=h[r]; right[left[h[r]]]=id; left[h[r]]=id; } } void remove(int c){ for(int i=down[c];i!=c;i=down[i]){ right[left[i]]=right[i]; left[right[i]]=left[i]; } } void resume(int c){ for(int i=down[c];i!=c;i=down[i]){ right[left[i]]=i; left[right[i]]=i; } } bool v[maxM]; int f(){//最少的选择行数,把所有列数覆盖 int cnt=0; for(int c=right[0];c!=0;c=right[c]) v[c]=true; for(int c=right[0];c!=0;c=right[c]){ if( v[c] ){ v[c]=false; cnt++; for(int i=down[c];i!=c;i=down[i]) for(int j=right[i];j!=i;j=right[j]) { v[ col[j] ]=false; } } } return cnt; } bool dance(int d){ if( d+f()>k ) return false; if( right[0]==0 ) return true; int c=right[0]; for(int i=c;i!=0;i=right[i]) if( s[i]<s[c] ) c=i; for(int i=down[c];i!=c;i=down[i]){ remove(i); for(int j=right[i];j!=i;j=right[j]) remove(j); if( dance(d+1) ) return true; for(int j=left[i];j!=i;j=left[j]) resume(j); resume(i); } return false; } }dlx; int main(){ ios::sync_with_stdio(false); int t; cin>>t; while(t--){ //每个city是col,radar是row //重复覆盖 int n,m,k; cin>>n>>m>>k; for(int i=1;i<=n;i++) cin>>city1[i]>>city2[i]; for(int i=1;i<=m;i++) cin>>radar1[i]>>radar2[i]; for(int i=1;i<=m;i++) for(int j=1;j<=n;j++){ dist[i][j] = sqrt( (city1[j]-radar1[i])*(city1[j]-radar1[i]) + (city2[j]-radar2[i])*(city2[j]-radar2[i]) ); } double start=0,end=1416; while( end-start>eps ){ dlx.init(m,n,k); double mid=(start+end)/2; for(int i=1;i<=m;i++){ for(int j=1;j<=n;j++){//can radar station i reach city j if( mid>=dist[i][j] ) dlx.link(i,j); } } if( dlx.dance(0) ) end=mid; else start=mid; } cout<<fixed<<setprecision(6)<<end<<endl; } return 0; }
========================================================================================================================================
【POJ 1084 Square Destroyer】
建模很容易就能说清楚,但我一直想不出来。
要问为什么的话可能是因为这题要先预处理出来所有正方形,而我没做过要预处理的舞蹈链的题。所以想不到。
那就是预处理出来所有正方形,用一个long long来表示一个正方形,这个正方形有没有包含id这条边就用 (1<<id)&num判断。那怎么预处理所有正方形呢,枚举边长1-n的正方形,然后再枚举这个正方形左上方的顶点就能做出来。
然后就能建模了,火柴是行,所有按现有火柴能拼出来的正方形是列,与其说是精准覆盖倒也可以说是全部破坏。
http://exp-blog.com/2018/06/11/pid-113/
读到这篇文章,打算模板还是多写几遍吧。于是这个模板就是自己手写的,debug了一整个下午。最后发现我把 l[size] = l[h[r]]写成了l[size]=R[h[r]]。有点气。。
再说一下f()函数,加不加都ac了,耗时也差不多。但是有的题还是会卡这点常数。【后来发现f()这个函数很重要,算是三大优化吧(反着恢复、从元素少的列开始搜、f()优化),每一个都至关重要 】
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<map> #include<iomanip> #include<algorithm> #include<vector> #define INF 2e9 #define maxnode 5000 using namespace std; int empty[70]; long long square[200];//这个正方形需要哪些火柴 struct DLX{ int n,m,size; int up[maxnode],down[maxnode],R[maxnode],l[maxnode]; int h[70],s[200],row[maxnode],col[maxnode]; int ansd; void init(int n1,int m1){ n=n1; m=m1; ansd=-1; for(int i=0;i<=m;i++){ up[i] = down[i] = i; l[i] = i-1; R[i] = i+1; s[i]=0; } size=m; l[0]=m; R[m]=0; for(int i=1;i<=n;i++) h[i]=-1; } void link(int r,int c){ row[ ++size ] = r; col[size]=c; s[c]++; up[size] = down[c]; down[size]=c; down[up[c]]=size; up[c]=size; if( h[r]==-1 ) l[size]=R[size]=h[r]=size; else{ l[size]=l[h[r]]; R[size]=h[r]; R[l[h[r]]]=size; l[h[r]]=size; } } void remove(int c){ for(int i=down[c];i!=c;i=down[i]){ R[l[i]] = R[i]; l[R[i]] = l[i]; } } void resume(int c){ for(int i=down[c];i!=c;i=down[i]){ R[l[i]]=i; l[R[i]]=i; } } bool vis[200]; int f(){ int ret=0; for(int i=R[0];i!=0;i=R[i]) vis[i]=true; for(int i=R[0];i!=0;i=R[i]){ if( vis[i] ){ ret++; vis[i]=false; for(int j=down[i];j!=i;j=down[j]){ for(int k=R[j];k!=j;k=R[k]) vis[ col[k] ]=false; } } } return ret; } void dance(int d){ if( ansd!=-1 && d>=ansd ) return; if( R[0]==0 ){ ansd=d; return; } int c=R[0]; for(int i=R[c];i!=0;i=R[i]){ if( s[i]<s[c] ) c=i; } for(int i=down[c];i!=c;i=down[i]){ remove(i); for(int j=R[i];j!=i;j=R[j]) remove(j); dance(d+1); for(int j=R[i];j!=i;j=R[j]) resume(j); resume(i); } } }dlx; int main(){ ios::sync_with_stdio(false); int t; cin>>t; while(t--){ memset(empty,0,sizeof(empty)); int n,k1; cin>>n>>k1; for(int i=1;i<=k1;i++){ int id; cin>>id; empty[id]=1; } int cnt=0;//有这么多个正方形 for(int i=1;i<=n;i++){//搜索长度为i的正方形 for(int j=0;j<=n-i;j++){ for(int k=0;k<=n-i;k++){ //正方形的左上角在(j,k) //这样构造出来的正方形要哪些火柴 long long num=0; bool configure=true; //上边 int id=k*(n+n+1)+j; for(int j1=1;j1<=i;j1++){ id+=1; if( empty[id] ) configure=false; num+= (long long)1<<id; } //左边 id=k*(n+n+1)+j-n; for(int k1=1;k1<=i;k1++){ id+=(n+n+1); if( empty[id] ) configure=false; num+= (long long)1<<id; } //下边 id=(k+i)*(n+n+1)+j; for(int j1=1;j1<=i;j1++){ id++; if( empty[id] ) configure=false; num+= (long long)1<<id; } //右边 id=k*(n+n+1)+j+i-n; for(int k1=1;k1<=i;k1++){ id+=n+n+1; if( empty[id] ) configure=false; num+= (long long)1<<id; } if( configure ) square[++cnt]=num; } } } dlx.init(2*n*(n+1),cnt); for(int i=1;i<=2*n*(n+1);i++){ for(int j=1;j<=cnt;j++){ if( ((long long)1<<i)&square[j] ) dlx.link(i,j); } } dlx.dance(0); cout<<dlx.ansd<<endl; } return 0; }
========================================================================================================================================
POJ 3074 Sudoku
算是舞蹈链的一个典型应用吧,这次我手写又手写残了,犯了个小错误,debug一整年
还没有一次写对过,下次争取。。
建模比较巧妙但很好理解,列是完成数独的所有条件(一共81*4=324个),行是每个格子上填几(根据建模每个行恰好满足四个列的约束条件:宫里有num,行里有num,列里有num,格子里有数)。解就是挑81个行满足所有的列。
建模思路在这:https://www.cnblogs.com/grenet/p/3163550.html
其中还解释了为什么dance时要从元素少的列开始搜,而不是直接Right[0],虽然只是一个直觉上的解释:
我觉得可以这么理解,dance(k+1)是一个比dance(k+2)要难解决的问题。(因为dance(k+1)然后再pick一个行,得到dance(k+2)这个子问题)
那简单地说就是优化前最差做8次dance(k+1),32次dance(k+2);优化后最差做4次dance(k+1),32次dance(k+2)
所以这实际上是一种贪心,每一次递归用尽量少的代价做【转移】,那对于k越少的时候我们就希望它转移的次数越少,因为k越少问题越复杂剩下的结点就越多每一次remove和resume的代价就大,所以我们每次都选元素最少的列。
这么优化省下来一个巨大的常数复杂度
blog之后还讲了“数独的舞蹈链”算法,基于数独这个具体问题又进行了优化,感兴趣的可以看一下。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<map> #include<iomanip> #include<algorithm> #include<vector> #define INF 2e9 #define maxnode 100000 using namespace std; struct DLX{ int n,m,size; int up[maxnode],down[maxnode],R[maxnode],L[maxnode]; int h[5000],s[500],row[maxnode],col[maxnode]; int ansd,ans[5000]; void init(int n1,int m1){ n=n1; m=m1; for(int i=0;i<=m;i++){ up[i]=down[i]=i; R[i]=i+1; L[i]=i-1; s[i]=0; } L[0]=m; R[m]=0; size=m; for(int i=1;i<=n;i++) h[i]=-1; } void link(int r,int c){ // cout<<R[0]<<endl; // cout<<r<<" "<<c<<endl; row[++size]=r; col[size]=c; s[c]++; up[size]=up[c]; down[size]=c; down[up[c]]=size; up[c]=size; if( h[r]==-1 ) h[r]=L[size]=R[size]=size; else{ L[size]=L[h[r]]; R[size]=h[r]; R[L[h[r]]]=size; L[h[r]]=size; } // cout<<R[0]<<endl; } void remove(int c){ L[R[c]]=L[c]; R[L[c]]=R[c]; for(int i=down[c];i!=c;i=down[i]){ for(int j=R[i];j!=i;j=R[j]){ down[up[j]] = down[j]; up[down[j]] = up[j]; s[ col[j] ]--; } } } void resume(int c){ L[R[c]]=c; R[L[c]]=c; for(int i=down[c];i!=c;i=down[i]){ for(int j=R[i];j!=i;j=R[j]){ down[up[j]]=j; up[down[j]]=j; s[ col[j] ]++; } } } bool dance(int d){ // cout<<R[0]<<" "; if( R[0]==0 ){ ansd=d; return true; } int c=R[0]; for(int i=R[c];i!=0;i=R[i]){ // cout<<i<<" "; if( s[i]<s[c] ) c=i; } //cout<<c<<" "<<d<<endl; remove(c); for(int i=down[c];i!=c;i=down[i]){ ans[d]=row[i];// cout<<i<<endl; for(int j=R[i];j!=i;j=R[j]) remove( 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; struct node{ int r,c,num; node(int r1=0,int c1=0,int n1=0): r(r1),c(c1),num(n1) {} }mp[5000]; int shudu[10][10]; int main(){ ios::sync_with_stdio(false); // 一个数独的完成代表着 81*4 = 324个约束条件的满足 // 1-81代表这个(1+id/9,id%9)格子里有一个数 // (1-81) +81 代表第(id/9)+1个行里面有没有数字id%9 // (1-81) +162代表第(id/9)+1个列里面有没有数字id%9 // (1-81) +243代表第(id/9)+1个宫里有没有数字id%9 //每一行代表一个格子的填法 while(1){ string s; cin>>s; if(s=="end") break; dlx.init(5000,324);//行多了无所谓 int id=0; for(int i=0;i<=80;i++){ //这个格子的位置 int r=1+i/9,c=1+i%9,gong; int x=r,y=c; x--; y--; x/=3; y/=3; x++; y++; if(x==1){ if(y==1) gong=1; else if(y==2) gong=2; else gong=3; } else if(x==2){ if(y==1) gong=4; else if(y==2) gong=5; else gong=6; } else{ if(y==1) gong=7; else if(y==2) gong=8; else gong=9; } //cout<<i<<" "<<s[i]<<" "<<r<<" "<<c<<" "<<gong<<endl; //cout<<"!!! "<<dlx.R[0]<<endl; if( s[i]=='.' ){ for(int j=1;j<=9;j++){//枚举能填的数 id++; dlx.link(id,(r-1)*9+c); dlx.link(id,81+(r-1)*9+j); dlx.link(id,162+(c-1)*9+j); dlx.link(id,243+(gong-1)*9+j); mp[id]=node(r,c,j); } } else { int num=int(s[i])-48; dlx.link( ++id,(r-1)*9+c ); dlx.link(id,81+(r-1)*9+num); dlx.link(id,162+(c-1)*9+num); dlx.link(id,243+(gong-1)*9+num); mp[id]=node(r,c,num); } } //cout<<"???"<<endl; dlx.dance(0); //cout<<"!!! "<<dlx.ansd<<endl; for(int i=0;i<dlx.ansd;i++){ node p=mp[ dlx.ans[i] ]; //cout<<dlx.ans[i]<<endl; shudu[p.r][p.c]=p.num; } for(int i=0;i<=80;i++){ int r=1+i/9,c=1+i%9; if(s[i]=='.') cout<<char(shudu[r][c]+48); else cout<<s[i]; } cout<<endl; } return 0; }
========================================================================================================================================
【ZOJ 3122 Sudoku】
关于resume我是这么写的:
kuangbin是这么写的:
按他这么写的话那就是严格意义的倒着恢复回去,但我觉得这对复杂度没有影响,因为它只是恢复上下链,不会影响到双重循环时候对于左右链的遍历。所以我就试了一下,发现确实不影响。
建模思路跟普通的一样。输入输出比算法难写
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<map> #include<iomanip> #include<algorithm> #include<vector> #define INF 2e9 #define maxnode 100000 using namespace std; struct DLX{ int up[maxnode],down[maxnode],left[maxnode],right[maxnode]; int row[maxnode],col[maxnode],s[1050],h[5000]; int n,m,size,ansd,ans[5000]; void init(int n1,int m1){ n=n1; m=m1; for(int i=0;i<=m;i++){ left[i] = i-1; right[i] = i+1; up[i] = down[i] = i; s[i]=0; } left[0]=m; right[m]=0; size=m; for(int i=1;i<=n;i++) h[i]=-1; } void link(int r,int c){ row[++size]=r; col[size]=c; s[c]++; up[size]=up[c]; down[size]=c; down[ up[c] ]=size; up[c]=size; if( h[r]==-1 ) left[size]=right[size]=h[r]=size; else{ left[size]=left[h[r]]; right[size]=h[r]; right[left[h[r]]]=size; left[h[r]]=size; } } void remove(int c){ //cout<<"!!! "<<c<<endl; right[left[c]]=right[c]; left[right[c]]=left[c]; for(int i=down[c];i!=c;i=down[i]){ for(int j=right[i];j!=i;j=right[j]){ up[down[j]]=up[j]; down[up[j]]=down[j]; s[ col[j] ]--; } } } void resume(int c){ right[left[c]]=c; left[right[c]]=c; for(int i=down[c];i!=c;i=down[i]){ for(int j=right[i];j!=i;j=right[j]){ up[down[j]]=j; down[up[j]]=j; s[ col[j] ]++; } } } bool dance(int d){ if( right[0]==0 ) return true; int c=right[0]; for(int i=c;i!=0;i=right[i]){ if( s[i]<s[c] ) c=i; } remove(c); for(int i=down[c];i!=c;i=down[i]){ ans[d] = row[i];//行的编号 for(int j=right[i];j!=i;j=right[j]) remove( col[j] ); if( dance(d+1) ) return true; for(int j=left[i];j!=i;j=left[j]) resume( col[j] ); } resume(c); return false; } }dlx; struct node{ int r,c,num; node(int r1=0,int c1=0,int n1=0): r(r1),c(c1),num(n1) {} }biao[5000]; char board[17][17]; int main(){ ios::sync_with_stdio(false); //A->1 P->16 //1-256 这个格子里有没有数 //256 + 1-256 这一行里有没有数字x //512 + 1-256 这一列里有没有数字x //768 + 1-256 这个宫里有没有数字x int ca=0; while(1){ int count=0; dlx.init(5000,1024); for(int i=0;i<16;i++){ if( scanf("%s",board[i])==EOF ) return 0; for(int j=0;j<16;j++){ int r=i/4,c=j/4; int gong=r*4+c; if(board[i][j]=='-'){ for(int k=1;k<=16;k++){ count++; dlx.link(count,i*16+j+1); dlx.link(count,256+i*16+k); dlx.link(count,512+j*16+k); dlx.link(count,768+gong*16+k); biao[count]=node(i,j,k); } } else{ int num=int(board[i][j])-64; count++; dlx.link( count,i*16+j+1 ); dlx.link( count,256+i*16+num); dlx.link( count,512+j*16+num); dlx.link( count,768+gong*16+num ); biao[count]=node(i,j,num); } } } dlx.dance(0); for(int i=0;i<256;i++){ node p=biao[ dlx.ans[i] ]; board[ p.r ][ p.c ] = char(p.num+64); } if( ca++ ) cout<<endl; for(int i=0;i<16;i++){ for(int j=0;j<16;j++) cout<<board[i][j]; cout<<endl; } } return 0; }
========================================================================================================================================
【HDU 4069 Squiggly Sudoku】
建模思路跟之前的一样,宫的话dfs搜索一下找联通分量就行,好像也没有更好的办法
因为舞蹈链一般找到解以后就直接跳出了,所以ans数组就是ans不会再变。但这题让找一下有没有多组解,所以就不能找到一个解后直接跳出。就有一个小坑是都搜完后ans数组可能不是合法ans,所以找到第一个解的时候要单独存一下才能ac
但这个样例就是如果中间不存一下ans数组的话,样例还是能过。。。
那应该就是正好最后一次枚举的时候找到了这个解??(所以样例挺坑的啊)
不然的话它回溯的时候枚举下一个可能会覆盖原来ans数组的
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<map> #include<iomanip> #include<algorithm> #include<vector> #define INF 2e9 #define maxnode 100000 using namespace std; int dx[4]={0,0,1,-1}; int dy[4]={1,-1,0,0}; int board[10][10],vis[100],an[100]; vector<int> edge[100]; int cnt; void dfs(int u){ vis[u]=cnt; for(int i=0;i<edge[u].size();i++){ int v=edge[u][i]; if( !vis[v] ) dfs(v); } } struct DLX{ int up[maxnode],down[maxnode],L[maxnode],R[maxnode]; int row[maxnode],col[maxnode],h[5000],s[350]; int n,m,size,ansd,ans[5000]; void init(int n1,int m1){ n=n1; m=m1; for(int i=0;i<=m;i++){ up[i]=down[i]=i; L[i]=i-1; R[i]=i+1; s[i]=0; } L[0]=m; R[m]=0; ansd=0; size=m; for(int i=1;i<=n;i++) h[i]=-1; } void link(int r,int c){ row[++size]=r; col[size]=c; s[c]++; up[size]=up[c]; down[size]=c; down[up[c]]=size; up[c]=size; if( h[r]==-1 ) h[r] = L[size] = R[size] = size; else{ L[size]=L[h[r]]; R[size]=h[r]; R[L[h[r]]]=size; L[h[r]]=size; } } void remove(int c){ L[R[c]]=L[c]; R[L[c]]=R[c]; for(int i=down[c];i!=c;i=down[i]){ for(int j=R[i];j!=i;j=R[j]){ up[down[j]]=up[j]; down[up[j]]=down[j]; s[ col[j] ]--;//忘写这一行的话会慢很多但不会错 } } } void resume(int c){ L[R[c]]=c; R[L[c]]=c; for(int i=down[c];i!=c;i=down[i]){ for(int j=R[i];j!=i;j=R[j]){ up[down[j]]=j; down[up[j]]=j; s[ col[j] ]++; } } } bool dance(int d){ // cout<<d<<" "<<R[0]<<endl; if( R[0]==0 ){ if( ansd!=0 ) return true; ansd=d; for(int i=0;i<81;i++) an[i]=ans[i]; return false;//目前还没有搜到multiple solution } int c=R[0]; for(int i=c;i!=0;i=R[i]) if( s[i]<s[c] ) c=i; remove(c); for(int i=down[c];i!=c;i=down[i]){ ans[d]=row[i]; for(int j=R[i];j!=i;j=R[j]) remove( 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; struct node{ int r,c,num; node(int r1=0,int c1=0,int n1=0): r(r1),c(c1),num(n1) {} }biao[5000]; int main(){ ios::sync_with_stdio(false); int t; cin>>t; int cas=0; while(t--){ memset(vis,0,sizeof(vis)); for(int i=0;i<100;i++) edge[i].clear(); cnt=0; for(int i=0;i<9;i++){ for(int j=0;j<9;j++){ int id=i*9+j+1; cin>>board[i][j]; if( board[i][j]>=128 ) board[i][j]-=128;//左边 else edge[id].push_back( id-1 ); if( board[i][j]>=64 ) board[i][j]-=64; else edge[id].push_back( id+9 );//down if( board[i][j]>=32 ) board[i][j]-=32;//right else edge[id].push_back( id+1 ); if( board[i][j]>=16 ) board[i][j]-=16;//up else edge[id].push_back( id-9 ); } } for(int i=1;i<=81;i++){ if( !vis[i] ){ cnt++; dfs(i); } } //这样一来就都分好了宫 dlx.init(4000,324); int rows=0; for(int i=0;i<9;i++){ for(int j=0;j<9;j++){ int id=i*9+j+1; if( board[i][j]==0 ){ for(int k=1;k<=9;k++){ rows++; dlx.link(rows,id); dlx.link(rows,81+i*9+k);//行 dlx.link(rows,162+j*9+k);//列 dlx.link(rows,243+(vis[id]-1)*9+k); biao[rows]=node(i,j,k); // cout<<rows<<" "<<id<<" "<<81+i*9+k <<" "<<162+j*9+k <<" "<<243+(vis[id]-1)*9+k<<endl; } } else{ rows++; dlx.link(rows,id); dlx.link(rows,81+i*9+board[i][j]); dlx.link(rows,162+j*9+board[i][j]); dlx.link(rows,243+(vis[id]-1)*9+board[i][j]); biao[rows]=node(i,j,board[i][j]); // cout<<rows<<" "<<id<<" "<<81+i*9+board[i][j] <<" "<<162+j*9+board[i][j] <<" "<<243+(vis[id]-1)*9+board[i][j]<<endl; } } } cout<<"Case "<<++cas<<":"<<endl; if( dlx.dance(0) ) cout<<"Multiple Solutions"<<endl; else if(dlx.ansd){ for(int i=0;i<=80;i++){ node p=biao[ an[i] ]; board[p.r][p.c]=p.num; } for(int i=0;i<9;i++){ for(int j=0;j<9;j++) cout<<board[i][j]; cout<<endl; } } else cout<<"No solution"<<endl; } return 0; }
========================================================================================================================================
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<map> #include<iomanip> #include<algorithm> #include<vector> #define INF 2e9 #define maxnode 100000 int dx[4]={0,0,1,-1}; int dy[4]={1,-1,0,0}; using namespace std; struct DLX{ int n,m,size,ansd; int up[maxnode],down[maxnode],L[maxnode],R[maxnode]; int row[maxnode],col[maxnode],h[1005],s[1005]; void init(int n1,int m1){ n=n1; m=m1; for(int i=0;i<=m;i++){ L[i]=i-1; R[i]=i+1; up[i] = down[i] = i; s[i]=0; } L[0]=m; R[m]=0; size=m; ansd=0; for(int i=1;i<=n;i++) h[i]=-1; } void link(int r,int c){ //cout<<r<<" "<<c<<" "<<size+1<<endl; row[++size]=r; col[size]=c; s[c]++; up[size]=up[c]; down[size]=c; down[up[c]]=size; up[c]=size; if( h[r]==-1 ) h[r] = L[size] = R[size] = size; else{ L[size]=L[h[r]]; R[size]=h[r]; R[L[h[r]]]=size; L[h[r]]=size; } } void remove(int c){ for(int i=down[c];i!=c;i=down[i]){ L[R[i]]=L[i]; R[L[i]]=R[i]; } } void resume(int c){ for(int i=down[c];i!=c;i=down[i]){ L[R[i]]=i; R[L[i]]=i; } } void dance(int d){ if( R[0]==0 ){ ansd=max(ansd,d); return; } int c=R[0]; for(int i=R[c];i!=0;i=R[i]) if( s[i]<s[c] ) c=i; for(int i=down[c];i!=c;i=down[i]){ remove(i); for(int j=R[i];j!=i;j=R[j]) remove(j); dance(d+1); for(int j=L[i];j!=i;j=L[j]) resume(j); resume(i); //对于这一列要重复覆盖 } } }dlx; long long a[1005]; int main(){ ios::sync_with_stdio(false); int t; cin>>t; while(t--){ int n; cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; dlx.init(n,n); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if( a[i]%a[j]==0 || a[j]%a[i]==0 ){ dlx.link(i,j); } dlx.dance(0); cout<<dlx.ansd<<endl; } return 0; }
========================================================================================================================================
【HDU 4979 A simple math problem】
做的第一道打表题,爽!
想到可以用2进制表示某个数有没有选,那就能从1到(1<<8)遍历所有的挑数情况了。
然后对于每个挑数情况,我们把这个数存下来,因为在之后建图的时候会用到
建图时候就双重循环,对于C(N,M)每个从n个数买m个数的可能,枚举C(N,R)个二等奖的可能。每个可能都是之前枚举的二进制数
买的可能&二等奖的可能=二等奖的可能,就代表你这么买可以覆盖这样的一个二等奖,那就建边就行了。
注意 f() 的优化非常重要,加了以后15分钟左右跑完了,不加的话我等了40分钟都没跑完。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<map> #include<iomanip> #include<algorithm> #include<vector> #define INF 2e9 #define maxnode 100000 #define MP make_pair #define ll long long int dx[4]={0,0,1,-1}; int dy[4]={1,-1,0,0}; using namespace std; int c[10][10];//c[i][j],i个数里挑j个的方案数 int combi[10][10][ (1<<10) ];//存了所有组合数 struct DLX{ int n,m,size,ansd; int up[maxnode],down[maxnode],L[maxnode],R[maxnode]; int row[maxnode],col[maxnode],h[500],s[5000]; void init(int n1,int m1){ n=n1; m=m1; for(int i=0;i<=m;i++){ up[i]=down[i]=i; L[i]=i-1; R[i]=i+1; s[i]=0; } L[0]=m; R[m]=0; size=m; ansd=0; for(int i=1;i<=n;i++) h[i]=-1; } void link(int r,int c){ row[++size]=r; col[size]=c; s[c]++; up[size]=up[c]; down[size]=c; down[up[c]]=size; up[c]=size; if( h[r]==-1 ) h[r] = L[size] = R[size] =size; else{ L[size]=L[h[r]]; R[size]=h[r]; R[L[h[r]]]=size; L[h[r]]=size; } } void remove(int c){ for(int i=down[c];i!=c;i=down[i]){ L[R[i]]=L[i]; R[L[i]]=R[i]; } } void resume(int c){ for(int i=down[c];i!=c;i=down[i]) L[R[i]] = R[L[i]] = i; } int f(){ int vis[5000]; for(int i=R[0];i!=0;i=R[i]) vis[i]=0; int ret=0; for(int i=R[0];i!=0;i=R[i]){ if( !vis[i] ){ vis[i]=1; ret++; for(int j=down[i];j!=i;j=down[j]){ for(int k=R[j];k!=j;k=R[k]){ vis[ col[k] ]=1; } } } } return ret; } void dance(int d){ if( ansd && d+f()>=ansd ) return; if( R[0]==0 ){ ansd=d; return; } //cout<<d<<" "<<R[0]<<endl; int c=R[0]; for(int i=c;i!=0;i=R[i]){ if( s[i]<s[c] ) c=i; } for(int i=down[c];i!=c;i=down[i]){ remove(i); for(int j=R[i];j!=i;j=R[j]) remove(j); dance(d+1); for(int j=L[i];j!=i;j=L[j]) resume(j); resume(i); } } }dlx; void build(int i1,int j1,int k1){//一共i个数,可以挑j个数,中奖号码是k位数 for(int i=1;i<=c[i1][j1];i++){ for(int j=1;j<=c[i1][k1];j++){ if( (combi[i1][j1][i]&combi[i1][k1][j])==combi[i1][k1][j] ) dlx.link( i,j );//说明覆盖了 } } } int main(){ ios::sync_with_stdio(false); for(int i=1;i<(1<<8);i++){//所有8位能组出来的组合,C(n,几)的所有可能性 //算一下挑了多少个数 int cnt=0,k; for(int j=0;(1<<j)<=i;j++){ if( (1<<j)&i ) cnt++; k=j;//当前i的最大位是第几位 } for(int j=k+1;j<=8;j++){ c[j][cnt]++; combi[j][cnt][ c[j][cnt] ]=i; } } //把所有的排列都搞出来了 for(int i=1;i<=8;i++){ cout<<"{"<<endl; for(int j=1;j<=i;j++){ cout<<" {"; for(int k=1;k<=j;k++){//中奖号码是k位数 dlx.init(c[i][j],c[i][k]); build(i,j,k); dlx.dance(0); cout<<dlx.ansd<<", "; // cout<<i<<" "<<j<<" "<<k<<" "<<dlx.ansd<<endl; } cout<<"}"<<endl; } cout<<"}"<<endl; } return 0; }
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<map> #include<iomanip> #include<algorithm> #include<vector> #define INF 2e9 #define maxnode 100000 #define MP make_pair #define ll long long int dx[4]={0,0,1,-1}; int dy[4]={1,-1,0,0}; using namespace std; int ans[8][8][8]= { { {1} }, { {2 }, {1, 1 } }, { {3, }, {2, 3, }, {1, 1, 1 } }, { {4 }, {2, 6 }, {2, 3, 4 }, {1, 1, 1, 1 } }, { {5 }, {3, 10 }, {2, 4, 10 }, {2, 3, 4, 5 }, {1, 1, 1, 1, 1 } }, { {6 }, {3, 15 }, {2, 6, 20 }, {2, 3, 6, 15 }, {2, 3, 4, 5, 6 }, {1, 1, 1, 1, 1, 1 } }, { {7 }, {4, 21 }, {3, 7, 35 }, {2, 5, 12, 35 }, {2, 3, 5, 9, 21 }, {2, 3, 4, 5, 6, 7 }, {1, 1, 1, 1, 1, 1, 1 } }, { {8 }, {4, 28}, {3, 11, 56}, {2, 6, 14, 70 }, {2, 4, 8, 20, 56 }, {2, 3, 4, 7, 12, 28 }, {2, 3, 4, 5, 6, 7, 8 }, {1, 1, 1, 1, 1, 1, 1, 1 } } }; int main(){ ios::sync_with_stdio(false); int t; cin>>t; int ca=0; while(t--){ int n,m,r; cin>>n>>m>>r; cout<<"Case #"<<++ca<<": "<<ans[n-1][m-1][r-1]<<endl; } return 0; }
========================================================================================================================================
【HDU 5046 Airport】
建模思路跟radar一样,可以说很简单了。
有一个坑点是在算距离的时候要用long long,我一开始用int所以wa了好久
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<map> #include<iomanip> #include<algorithm> #include<vector> #define INF 2e9 #define maxnode 100000 #define MP make_pair #define ll long long int dx[4]={0,0,1,-1}; int dy[4]={1,-1,0,0}; using namespace std; long long x[70],y[70]; long long dist[70][70]; struct DLX{ int n,m,size,k;//k is the maximum degree int up[maxnode],down[maxnode],L[maxnode],R[maxnode],row[maxnode],col[maxnode]; int h[500],s[5000]; int ansd; void init(int n1,int m1,int k1){ n=n1; m=m1; k=k1; for(int i=0;i<=m;i++){ up[i] = down[i] = i; L[i]=i-1; R[i]=i+1; s[i]=0; } L[0]=m; R[m]=0; size=m; for(int i=1;i<=n;i++) h[i]=-1; } void link(int r,int c){ row[++size]=r; col[size]=c; s[c]++; up[size]=up[c]; down[size]=c; down[up[c]]=size; up[c]=size; if( h[r]==-1 ) h[r]=L[size]=R[size]=size; else{ L[size]=L[h[r]]; R[size]=h[r]; R[L[h[r]]]=size; L[h[r]]=size; } } void remove(int c){ for(int i=down[c];i!=c;i=down[i]){ L[R[i]]=L[i]; R[L[i]]=R[i]; } } void resume(int c){ for(int i=down[c];i!=c;i=down[i]) L[R[i]]=R[L[i]]=i; } int f(){ int vis[200]; for(int i=R[0];i!=0;i=R[i]) vis[i]=0; int ret=0; for(int i=R[0];i!=0;i=R[i]){ if( !vis[i] ){ vis[i]=1; ret++; for(int j=down[i];j!=i;j=down[j]) for(int k=R[j];k!=j;k=R[k]) vis[ col[k] ]=1; } } return ret; } bool dance(int d){ if( d+f()>k ) return false; if( R[0]==0 ) return true; int c=R[0]; for(int i=c;i!=0;i=R[i]) if( s[i]<s[c] ) c=i; for(int i=down[c];i!=c;i=down[i]){ remove(i); for(int j=R[i];j!=i;j=R[j]) remove(j); if( dance(d+1) ) return true; for(int j=L[i];j!=i;j=L[j]) resume(j); resume(i); } return false; } }dlx; int main(){ ios::sync_with_stdio(false); int t; cin>>t; int ca=0; while(t--){ int n,k; cin>>n>>k; for(int i=1;i<=n;i++) cin>>x[i]>>y[i]; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ long long xcha = x[i]-x[j]; if(xcha<0) xcha=-xcha; long long ycha = y[i]-y[j]; if(ycha<0) ycha=-ycha; dist[i][j] = xcha+ycha; } } long long start=0,mid,end=4e9; while( end>start ){ mid=(start+end)/2; dlx.init(n,n,k); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if( mid>=dist[i][j] ){ dlx.link(i,j); } } } if( dlx.dance(0) ) end=mid; else start=mid+1; } cout<<"Case #"<<++ca<<": "<<end<<endl; } return 0; }
========================================================================================================================================
【Regionals 2016: North America - Greater NY 7788 Tight-Fit Soduku】
这不是kuangbin系列的题,但也是我做的一道舞蹈链的题。
原本以为很简单就能做出来,但结果狂做了4个小时后才发现从根本上建模就建错了。
应当是1-54为格子里有没有数
54+(1-54)为列里面有没有数num
108+(1-54)为行里面有没有数num
162+(1-54)为宫里有没有数num
之后又发现输入输出很难,手握着答案输不出来。。。
后来解决的办法是将矩阵看成6*9的矩阵,所以这就要将输入时候的 j 和数字实际上所在的列 c 分离出来看。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<map> #include<iomanip> #include<algorithm> #include<vector> #define INF 2e9 #define maxnode 100000 #define MP make_pair #define ll long long int dx[4]={0,0,1,-1}; int dy[4]={1,-1,0,0}; using namespace std; struct DLX{ int up[maxnode],down[maxnode],left[maxnode],right[maxnode]; int row[maxnode],col[maxnode],s[1050],h[5000]; int n,m,size,ansd,ans[5000]; void init(int n1,int m1){ n=n1; m=m1; for(int i=0;i<=m;i++){ left[i] = i-1; right[i] = i+1; up[i] = down[i] = i; s[i]=0; } left[0]=m; right[m]=0; size=m; for(int i=1;i<=n;i++) h[i]=-1; } void link(int r,int c){ row[++size]=r; col[size]=c; s[c]++; up[size]=up[c]; down[size]=c; down[ up[c] ]=size; up[c]=size; if( h[r]==-1 ) left[size]=right[size]=h[r]=size; else{ left[size]=left[h[r]]; right[size]=h[r]; right[left[h[r]]]=size; left[h[r]]=size; } } void remove(int c){ right[left[c]]=right[c]; left[right[c]]=left[c]; for(int i=down[c];i!=c;i=down[i]){ for(int j=right[i];j!=i;j=right[j]){ up[down[j]]=up[j]; down[up[j]]=down[j]; s[ col[j] ]--; } } } void resume(int c){ right[left[c]]=c; left[right[c]]=c; for(int i=down[c];i!=c;i=down[i]){ for(int j=right[i];j!=i;j=right[j]){ up[down[j]]=j; down[up[j]]=j; s[ col[j] ]++; } } } bool dance(int d){ if( right[0]==0 ) return true; int c=right[0]; for(int i=c;i!=0;i=right[i]) if( s[i]<s[c] ) c=i; remove(c); for(int i=down[c];i!=c;i=down[i]){ ans[d] = row[i];//行的编号 for(int j=right[i];j!=i;j=right[j]) remove( col[j] ); if( dance(d+1) ) return true; for(int j=left[i];j!=i;j=left[j]) resume( col[j] ); } resume(c); return false; } }dlx; char board[10][10]; int slash[10][10]; struct node{ int r,c,num; node(int r1=0,int c1=0,int n1=0): r(r1),c(c1),num(n1) {} }biao[5000]; int main(){ //1-54 grid has number //54+(1-54) row has number num //108 + (1-54) col has number num //162+(1-54) 宫 has number num ios::sync_with_stdio(false); int t; cin>>t; while(t--){ int cnt=0; memset(slash,0,sizeof(slash)); int id1; cin>>id1; dlx.init(500,216); int id=0; for(int i=0;i<6;i++){ string s; int c=0; for(int j=0;j<6;j++){ cin>>s; int gong=(i/2)*2+(j/3); if( s.length()==3 ){ if( s[0]=='-' && s[2]=='-' ){ for(int k=1;k<=9;k++){ id++; dlx.link(id,i*9+c+1); dlx.link(id,54+i*9+k);//row dlx.link(id,108+j*9+k);//col dlx.link(id,162+gong*9+k); biao[id]=node(i,c,k); } for(int k=1;k<=9;k++){ id++; dlx.link(id,i*9+c+1+1); dlx.link(id,54+i*9+k);//row dlx.link(id,108+j*9+k);//col dlx.link(id,162+gong*9+k); biao[id]=node(i,c+1,k); } } else if( s[0]!='-' && s[2]!='-' ){ int num=int(s[0])-48; id++; dlx.link(id,i*9+c+1); dlx.link(id,54+i*9+num); dlx.link(id,108+j*9+num); dlx.link(id,162+gong*9+num); biao[id]=node(i,c,num); num=int(s[2])-48; id++; dlx.link(id,i*9+c+1+1); dlx.link(id,54+i*9+num); dlx.link(id,108+j*9+num); dlx.link(id,162+gong*9+num); biao[id]=node(i,c+1,num); } else if( s[0]=='-' && s[2]!='-' ){ int num=int(s[2])-48; //前面的要小于后面的 for(int k=1;k<num;k++){ id++; dlx.link(id,i*9+c+1); dlx.link(id,54+i*9+k);//row dlx.link(id,108+j*9+k);//col dlx.link(id,162+gong*9+k); biao[id]=node(i,c,k); } id++; dlx.link(id,i*9+c+1+1); dlx.link(id,54+i*9+num); dlx.link(id,108+j*9+num); dlx.link(id,162+gong*9+num); biao[id]=node(i,c+1,num); } else{ int num=int(s[0])-48; id++; dlx.link(id,i*9+c+1); dlx.link(id,54+i*9+num); dlx.link(id,108+j*9+num); dlx.link(id,162+gong*9+num); biao[id]=node(i,c,num); //第二个要大于第一个 for(int k=num+1;k<=9;k++){ id++; dlx.link(id,i*9+c+1+1); dlx.link(id,54+i*9+k);//row dlx.link(id,108+j*9+k);//col dlx.link(id,162+gong*9+k); biao[id]=node(i,c+1,k); } } slash[i][c]=1; board[i][c++]=s[0]; board[i][c++]=s[2]; } else { if( s[0]=='-' ){ for(int k=1;k<=9;k++){ id++; dlx.link(id,i*9+c+1); dlx.link(id,54+i*9+k);//row dlx.link(id,108+j*9+k);//col dlx.link(id,162+gong*9+k); biao[id]=node(i,c,k); } } else{ int num=int(s[0])-48; id++; dlx.link(id,i*9+c+1); dlx.link(id,54+i*9+num); dlx.link(id,108+j*9+num); dlx.link(id,162+gong*9+num); biao[id]=node(i,c,num); } board[i][c++]=s[0]; } } } dlx.dance(0); for(int i=0;i<54;i++){ node p=biao[ dlx.ans[i] ]; board[p.r][p.c]=char(48+p.num); } cout<<id1<<endl; for(int i=0;i<6;i++){ for(int j=0;j<9;j++){ if( slash[i][j] ) { if( board[i][j]>board[i][j+1] ){ char tmp=board[i][j+1]; board[i][j+1]=board[i][j]; board[i][j]=tmp; } cout<<board[i][j]<<"/"; } else cout<<board[i][j]<<" "; } cout<<endl; } } return 0; }
注:每题模板的dlx我都是手打出来的,前7次吧从来没有一次打对过,而且写的时候也非常慢。但到后面就很熟练了,我觉得打dlx模板还是让我收获了很多,想出了很多本质上的东西。比如如果我不手打的话,就不会想为什么kuangbin的模板快我的就慢。所以推荐每个学习dlx的人每次都重写一次模板,现在我能做到5分钟左右把dlx打出来因此根本不花多少时间。