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
",dlx.ansd);
    }
    return 0;    
}
View Code

========================================================================================================================================

【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;    

}
View Code

========================================================================================================================================

【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;    
}
View Code

========================================================================================================================================

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;    
}
View Code

========================================================================================================================================

【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;    
}

 
View Code

========================================================================================================================================

【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;    
}
View Code

========================================================================================================================================

【HDU 3335 Divisibility】
建模思路比较抽象,至今也不能严格证明正确性,但可以做到直觉上理解了。
理解的方法是自己造几个例子,然后想正解一定在重复覆盖的解集里面,而且重复覆盖的解集的行之间一定都不互相有整除关系
直觉上理解就行了,不要钻牛角尖
#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;    
}
View Code

========================================================================================================================================

【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;    
}
View Code
#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;
}
View Code

========================================================================================================================================

【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;
}
View Code

========================================================================================================================================

【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;    
}
View Code

注:每题模板的dlx我都是手打出来的,前7次吧从来没有一次打对过,而且写的时候也非常慢。但到后面就很熟练了,我觉得打dlx模板还是让我收获了很多,想出了很多本质上的东西。比如如果我不手打的话,就不会想为什么kuangbin的模板快我的就慢。所以推荐每个学习dlx的人每次都重写一次模板,现在我能做到5分钟左右把dlx打出来因此根本不花多少时间。

原文地址:https://www.cnblogs.com/ZhenghangHu/p/9752909.html