zoukankan      html  css  js  c++  java
  • 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打出来因此根本不花多少时间。

  • 相关阅读:
    NOI2015刷题记录
    [WC2013][UOJ58]糖果公园 莫队算法
    啦啦啦~
    完全平方数
    构建之法
    测试更新
    程序的测试
    程序的封装
    构建之法5.5-6-7章观后感
    给徐侃童鞋的一个汉堡
  • 原文地址:https://www.cnblogs.com/ZhenghangHu/p/9752909.html
Copyright © 2011-2022 走看看