zoukankan      html  css  js  c++  java
  • DLX专题总结

    其实我个人非常喜欢DLX.

    因为我认为他较为简单——建模 + DLX = AC!

    这里先分享一套我较为常用的模板:

    const int N = 9;
    const int maxn = N*N*N + 10;
    const int maxnode=maxn*4+maxn+10;
    const int INF=0x3f3f3f3f;
    
    struct DLX{
        #define FF(i,A,S) for(int i = A[S];i != S;i = A[i])
        int L[maxnode],R[maxnode],U[maxnode],D[maxnode];//每个点的左右上下指针,所在行列
        int sz,col[maxnode],row[maxnode],S[maxn],H[maxn];//H每行的头结点,S每列的节点数
        int ans[maxn],cnt;
        int out[maxn];
        int n,m;
    
        void init(int _n,int _m) {
            n=_n, m=_m;
            for(int i = 0;i <= m;i ++)
                S[i]=0,U[i]=D[i]=i,L[i]=i-1,R[i]=i+1;
            R[m] = 0, L[0] = sz = m;
            for(int i = 1;i <= n; ++i) H[i] = -1;
        }
    
        void link(int r,int c) {
            ++S[col[++sz]=c], row[sz] = r;
            D[sz] = D[c], U[D[c]] = sz;
            U[sz] = c, D[c] = sz;
            if(H[r] < 0) H[r] = L[sz] = R[sz] = sz;
            else R[sz]=R[H[r]], L[R[H[r]]]=sz,L[sz]= H[r],R[H[r]] = sz;
        }
        void del(int c){//精确覆盖,删除涉及C列的集合
            L[R[c]]=L[c],R[L[c]]=R[c];
            FF(i,D,c)FF(j,R,i)U[D[j]]=U[j],D[U[j]]=D[j],--S[col[j]];
        }
        void add(int c){  //精确覆盖,恢复涉及C列的集合
            R[L[c]]=L[R[c]]=c;
            FF(i,U,c)FF(j,L,i)++S[col[U[D[j]]=D[U[j]]=j]];
        }
        bool dance(int k){//精确覆盖
            //(剪枝)if(cnt != -1 && cnt <= d) return 0;//精确匹配输出最小
            if(!R[0]){
                //cnt=min(cnt,k);//精确匹配输出最小
                for(int i = 0;i < k; ++i)
                    out[(ans[i]-1)/N]=(ans[i]-1)%N+1; //数独输出
                cnt=k;
                return 1;
            }
            int c=R[0];FF(i,R,0)if(S[c]>S[i])c=i;
            del(c);
            FF(i,D,c){
                FF(j,R,i)del(col[j]);
                ans[k]=row[i];
                if(dance(k+1))return 1;
                //dance(k+1);//精确匹配输出最小
                FF(j,L,i)add(col[j]);
            }
            add(c);
            return 0;
        }
        void remove(int c){//重复覆盖
            FF(i,D,c)L[R[i]]=L[i],R[L[i]]=R[i];
        }
         void resume(int c){//重复覆盖
             FF(i,U,c)L[R[i]]=R[L[i]]=i;
        }
        int A(){//估价函数
            int res=0;
            bool vis[m+1];
            memset(vis,0,sizeof(vis));
            FF(i,R,0)if(!vis[i]){
                    res++,vis[i]=1;
                    FF(j,D,i)FF(k,R,j)vis[col[k]]=1;
                }
            return res;
        }
        void dance(int now,int &ans){//重复覆盖,需要传入一个接收答案的ans
            if(R[0]==0)ans=min(ans,now);
            else if (now+A()>=limit) return ;
            else if(now+A()<ans){
                int temp=INF,c;
                FF(i,R,0)if(temp>S[i])temp=S[i],c=i;
                FF(i,D,c){
                    remove(i);
                    FF(j,R,i)remove(j);
                    dance(now+1,ans);
                    FF(j,L,i)resume(j);
                    resume(i);
                }
            }
        }
    }dlx;
    View Code

    具体的原理我就不再说了,毕竟网上已经有很多优秀的博客。(其实就是我也不是很懂原理,就知道十字交叉链表

    这里主要是为了给我刷的kuangbin专题写个总结。顺便写写我个人对DLX的理解吧。

    (优化其实都在我给的板子里有所体现,这里就不说了)

    DLX最难的地方就是建模了。首先我们需要弄明白行跟列分别代表了什么:

    行——所有的发生情况  列——所有限制条件

    就拿三阶数独来举例吧。(POJ - 3074

    首先一个三阶数独,总共有81个格子。每个格子都可以摆上1-9

    所以,所有的发生情况就是9 x 81 = 729 也就是行数了。

     

    之后一个三阶数独有4个大的限制条件:

    一、所有的格子上必须都有数字摆放(81个格子)

    二、每一行上都要恰好有1-9这就个数字 (9行 X 9个数字 = 81)

    三、每一列上都要恰好有1-9这就个数字  (9列 X 9个数字 = 81)

    四、每一个宫里面都要恰好有1-9这就个数字  (9宫 X 9个数字 = 81)

    所以,所有的限制条件就有 (81 + 81 + 81 +81 )种, 也就是列数

    (这里放一张网上嫖来的图方便理解)

    之后解题思路就是,根据题目给出的数据,遍历矩阵里的每一个数字。

    分为两种情况:

    一、如果是题目给出的数字1-9

    (仅仅将这个情况LINK)

      LINK(行, 格子限制), LINK(行, 行限制), LINK(行, 列限制), LINK(行, 宫限制);

    二、如果是0

    (枚举每个数字在这个格子里的情况,将其LINK)

      for (int i = 1;i <= 9; ++i)

        LINK(行, 格子限制), LINK(行, 行限制), LINK(行, 列限制), LINK(行, 宫限制);

    之后DANCE就完事儿了。输出的话就是存的时候的行号处理一下输出就行。

    这里给出这道题的代码:

    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    using namespace std;
    typedef long long ll;
    
    const int N = 9;
    const int maxn = N*N*N + 10;
    const int maxnode=maxn*4+maxn+10;
    const int INF=0x3f3f3f3f;
    char g[maxn];
    
    inline void read(int &sum) {
        char ch = getchar();
        int tf = 0;
        sum= 0;
        while((ch < '0' || ch > '9') && (ch != '-')) ch = getchar();
        tf=((ch == '-') && (ch=getchar()));
        while(ch>='0' && ch<='9') sum=sum*10+(ch-48),ch=getchar();
        (tf) && (sum =- sum);
    }
    
    struct DLX{
        #define FF(i,A,S) for(int i = A[S];i != S;i = A[i])
    //    #define maxnode 50000
    //    #define maxn 750
    //    #define maxm 750
        int L[maxnode],R[maxnode],U[maxnode],D[maxnode];//每个点的左右上下指针,所在行列
        int sz,col[maxnode],row[maxnode],S[maxn],H[maxn];//H每行的头结点,S每列的节点数
        int ans[maxn],cnt;
        int out[maxn];
        int n,m;
    
        void init(int _n,int _m) {
            n=_n, m=_m;
            for(int i = 0;i <= m;i ++)
                S[i]=0,U[i]=D[i]=i,L[i]=i-1,R[i]=i+1;
            R[m] = 0, L[0] = sz = m;
            for(int i = 1;i <= n; ++i) H[i] = -1;
        }
    
        void link(int r,int c) {
            ++S[col[++sz]=c], row[sz] = r;
            D[sz] = D[c], U[D[c]] = sz;
            U[sz] = c, D[c] = sz;
            if(H[r] < 0) H[r] = L[sz] = R[sz] = sz;
            else R[sz]=R[H[r]], L[R[H[r]]]=sz,L[sz]= H[r],R[H[r]] = sz;
        }
        void del(int c){//精确覆盖,删除涉及C列的集合
            L[R[c]]=L[c],R[L[c]]=R[c];
            FF(i,D,c)FF(j,R,i)U[D[j]]=U[j],D[U[j]]=D[j],--S[col[j]];
        }
        void add(int c){  //精确覆盖,恢复涉及C列的集合
            R[L[c]]=L[R[c]]=c;
            FF(i,U,c)FF(j,L,i)++S[col[U[D[j]]=D[U[j]]=j]];
        }
        bool dance(int k){//精确覆盖
            //(剪枝)if(cnt != -1 && cnt <= d) return 0;//精确匹配输出最小
            if(!R[0]){
                //cnt=min(cnt,k);//精确匹配输出最小
                for(int i = 0;i < k; ++i)
                    out[(ans[i]-1)/9]=(ans[i]-1)%9+1;
                cnt=k;
                output();
                return 1;
            }
            int c=R[0];FF(i,R,0)if(S[c]>S[i])c=i;
            del(c);
            FF(i,D,c){
                FF(j,R,i)del(col[j]);
                ans[k]=row[i];
                if(dance(k+1))return 1;
                //dance(k+1);//精确匹配输出最小
                FF(j,L,i)add(col[j]);
            }
            add(c);
            return 0;
        }
    //    void remove(int c){//重复覆盖
    //        FF(i,D,c)L[R[i]]=L[i],R[L[i]]=R[i];
    //    }
    //     void resume(int c){//重复覆盖
    //         FF(i,U,c)L[R[i]]=R[L[i]]=i;
    //    }
    //    int A(){//估价函数
    //        int res=0;
    //        bool vis[m+1];
    //        memset(vis,0,sizeof(vis));
    //        FF(i,R,0)if(!vis[i]){
    //                res++,vis[i]=1;
    //                FF(j,D,i)FF(k,R,j)vis[col[k]]=1;
    //            }
    //        return res;
    //    }
    //    void dance(int now,int &ans){//重复覆盖,需要传入一个接收答案的ans
    //        if(R[0]==0)ans=min(ans,now);
    //        else if(now+A()<ans){
    //            int temp=INF,c;
    //            FF(i,R,0)if(temp>S[i])temp=S[i],c=i;
    //            FF(i,D,c){
    //                remove(i);
    //                FF(j,R,i)remove(j);
    //                dance(now+1,ans);
    //                FF(j,L,i)resume(j);
    //                resume(i);
    //            }
    //        }
    //    }
        void output() {
            for (int i = 0; i < cnt; ++i)
                cout << out[i];
            cout << endl;
        }
    
    }dlx;
    
    int main()
    {
        while(scanf("%s",g) == 1)
        {
            if(strcmp(g,"end") == 0) break;
            dlx.init(N * N * N,  N * N * 4);
            for (int i = 0; i < N; ++i)
                for (int j = 0; j < N; ++j)
                    for(int k = 1;k <= N;++k)
                        if(g[i * N + j] == '.' || g[i * N + j] == '0' + k)
                        {
                            dlx.link((i*N+j)*N+k, i*N+j+1);
                            dlx.link((i*N+j)*N+k, N*N+i*N+k);
                            dlx.link((i*N+j)*N+k, N*N*2+j*N+k);
                            dlx.link((i*N+j)*N+k, N*N*3+((i/3)*3+(j/3))*N+k);
                        }
            dlx.dance(0);
    
        }
        return 0;
    }
    View Code

    剩下的就都是题目的代码和建模思路了。

    先是精确覆盖,这东西非常的快。如果能转换成精确覆盖那么DLX必然是你的最佳选择。

    A - Treasure Map ZOJ - 3209

    这道题就是很经典的DLX精确覆盖裸题了。

    建模思路:

    行:所有的拼图数 P

    列:所有的面积 N*M(题目要求所有的面积都要被覆盖)

    给出代码:

    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    using namespace std;
    typedef long long ll;
    
    const int INF=0x3f3f3f3f;
    int n, m, p;
    
    inline void read(int &sum) {
        char ch = getchar();
        int tf = 0;
        sum= 0;
        while((ch < '0' || ch > '9') && (ch != '-')) ch = getchar();
        tf=((ch == '-') && (ch=getchar()));
        while(ch>='0' && ch<='9') sum=sum*10+(ch-48),ch=getchar();
        (tf) && (sum =- sum);
    }
    
    struct DLX{
        #define FF(i,A,s) for(int i = A[s];i != s;i = A[i])
        #define maxnode 500010
        #define maxn 550
        #define maxm 1050
        int L[maxnode],R[maxnode],U[maxnode],D[maxnode];//每个点的左右上下指针,所在行列
        int sz,col[maxnode],row[maxnode],s[maxm],H[maxn];//H每行的头结点,s每列的节点数
        int ans[maxm],cnt;
    
        void init(int m){
            for(int i=0;i<=m;++i)
                L[i]=i-1,R[i]=i+1,U[i]=D[i]=i;
            memset(H,-1,sizeof(H)),memset(s,0,sizeof(s));
            L[0]=m,R[m]=0,sz=m+1;
            cnt = INF;
        }
            void link(int r,int c)
        {
            ++ s[col[++ sz] = c];
            row[sz] = r;
            D[sz] = D[c];
            U[D[c]] = sz;
            U[sz] = c;
            D[c] = sz;
            if(H[r] < 0) H[r] = L[sz] = R[sz] = sz;
            else
            {
                R[sz] = R[H[r]];
                L[R[H[r]]] = sz;
                L[sz] = H[r];
                R[H[r]] = sz;
            }
        }
        void del(int c){//精确覆盖,删除涉及C列的集合
            L[R[c]]=L[c],R[L[c]]=R[c];
            FF(i,D,c)FF(j,R,i)U[D[j]]=U[j],D[U[j]]=D[j],--s[col[j]];
        }
        void add(int c){  //精确覆盖,恢复涉及C列的集合
            R[L[c]]=L[R[c]]=c;
            FF(i,U,c)FF(j,L,i)++s[col[U[D[j]]=D[U[j]]=j]];
        }
        bool dance(int k){//精确覆盖
            //if (cnt != -1)
            if(!R[0]){
                cnt=min(k, cnt);return 1;
            }
            int c=R[0];FF(i,R,0)if(s[c]>s[i])c=i;
            del(c);
            FF(i,D,c){
                FF(j,R,i)del(col[j]);
                ans[k]=row[i];
                dance(k+1);
                FF(j,L,i)add(col[j]);
            }
            add(c);
            return 0;
        }
        void remove(int c){//重复覆盖
            FF(i,D,c)L[R[i]]=L[i],R[L[i]]=R[i];
        }
         void resume(int c){//重复覆盖
             FF(i,U,c)L[R[i]]=R[L[i]]=i;
         }
        int A(){//估价函数
            int res=0;
            bool vis[m+1];
            memset(vis,0,sizeof(vis));
            FF(i,R,0)if(!vis[i]){
                    res++,vis[i]=1;
                    FF(j,D,i)FF(k,R,j)vis[col[k]]=1;
                }
            return res;
        }
        void dance(int now,int &ans){//重复覆盖,需要传入一个接收答案的ans
            if(R[0]==0)ans=min(ans,now);
            else if(now+A()<ans){
                int temp=INF,c;
                FF(i,R,0)if(temp>s[i])temp=s[i],c=i;
                FF(i,D,c){
                    remove(i); FF(j,R,i)remove(j);
                    dance(now+1,ans);
                    FF(j,L,i)resume(j); resume(i);
                }
            }
        }
        void output() {
    //        for (int i = 0; i < cnt; ++i)
    //            cout <<ans[i]<< ((i==cnt-1)?"
    ":" ");
            cout << cnt << endl;
        }
    }dlx;
    
    int main(){
        int T; read(T);
        while (T--) {
            read(n), read(m), read(p);
            dlx.init(n*m);
            int x1,x2,y1,y2;
            for (int i = 1; i <= p; ++i){
                read(x1),read(y1),read(x2),read(y2);
                for (int x = x1+1; x<= x2; ++x)
                    for (int y = y1+1; y <= y2; ++y)
                        dlx.link(i, y+(x-1)*m);
            }
            dlx.dance(0);
            cout << ((dlx.cnt==INF)?-1:dlx.cnt)<< endl;
        }
        return 0;
    }
    View Code

     

    B - Easy Finding POJ - 3740

    这道题就不讲了太裸了

    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    using namespace std;
    typedef long long ll;
    
    const int INF=0x3f3f3f3f;
    int n, m;
    
    inline bool read(int &sum) {
        char ch = getchar();
        int tf = 0;
        sum = 0;
        while((ch < '0' || ch > '9') && (ch != '-')) ch = getchar();
        tf = ((ch == '-') && (ch = getchar()));
        while(ch >= '0' && ch <= '9') sum = sum * 10+ (ch - 48), ch = getchar();
        (tf) && (sum =- sum);
        return 1;
    }
    
    struct DLX{
        #define FF(i,A,s) for(int i = A[s];i != s;i = A[i])
        #define maxn 6305
        int L[maxn],R[maxn],U[maxn],D[maxn];//每个点的左右上下指针,所在行列
        int sz,col[maxn],row[maxn],s[maxn],H[maxn];//H每行的头结点,s每列的节点数
        int ans[maxn],cnt;
    
        void init(int m){
            for(int i=0;i<=m;++i)
                L[i]=i-1,R[i]=i+1,U[i]=D[i]=i;
            memset(H,-1,sizeof(H)),memset(s,0,sizeof(s));
            L[0]=m,R[m]=0,sz=m+1;
        }
            void link(int r,int c)
        {
            ++ s[col[++ sz] = c];
            row[sz] = r;
            D[sz] = D[c];
            U[D[c]] = sz;
            U[sz] = c;
            D[c] = sz;
            if(H[r] < 0) H[r] = L[sz] = R[sz] = sz;
            else
            {
                R[sz] = R[H[r]];
                L[R[H[r]]] = sz;
                L[sz] = H[r];
                R[H[r]] = sz;
            }
        }
    
        void del(int c){//精确覆盖,删除涉及C列的集合
            L[R[c]]=L[c],R[L[c]]=R[c];
            FF(i,D,c)FF(j,R,i)U[D[j]]=U[j],D[U[j]]=D[j],--s[col[j]];
        }
        void add(int c){  //精确覆盖,恢复涉及C列的集合
            R[L[c]]=L[R[c]]=c;
            FF(i,U,c)FF(j,L,i)++s[col[U[D[j]]=D[U[j]]=j]];
        }
        bool dance(int k){//精确覆盖
            if(!R[0]){
                cnt=k;return 1;
            }
            int c=R[0];FF(i,R,0)if(s[c]>s[i])c=i;
            del(c);
            FF(i,D,c){
                FF(j,R,i)del(col[j]);
                ans[k]=row[i]; if(dance(k+1))return 1;
                FF(j,L,i)add(col[j]);
            }
            add(c);
            return 0;
        }
        void remove(int c){//重复覆盖
            FF(i,D,c)L[R[i]]=L[i],R[L[i]]=R[i];
        }
         void resume(int c){//重复覆盖
             FF(i,U,c)L[R[i]]=R[L[i]]=i;
         }
    //    int A(){//估价函数
    //        int res=0;
    //        bool vis[m+1];
    //        memset(vis,0,sizeof(vis));
    //        FF(i,R,0)if(!vis[i]){
    //                res++,vis[i]=1;
    //                FF(j,D,i)FF(k,R,j)vis[col[k]]=1;
    //            }
    //        return res;
    //    }
    //    void dance(int now,int &ans){//重复覆盖,需要传入一个接收答案的ans
    //        if(R[0]==0)ans=min(ans,now);
    //        else if(now+A()<ans){
    //            int temp=INF,c;
    //            FF(i,R,0)if(temp>s[i])temp=s[i],c=i;
    //            FF(i,D,c){
    //                remove(i);
    //                FF(j,R,i)remove(j);
    //                dance(now+1,ans);
    //                FF(j,L,i)resume(j);
    //                resume(i);
    //            }
    //        }
    //    }
    }dlx;
    
    int main(){
        cin.tie(0);
        ios::sync_with_stdio(0);
        while (cin >> n >> m) {
            dlx.init(m);
            int temp;
            for (int i = 1; i <= n; ++i)
                for (int j = 1; j <= m; ++j) {
                    read(temp);
                    if (temp) dlx.link(i,j);
                }
            if (dlx.dance(0)) cout << "Yes, I found it" << endl;
            else cout << "It is impossible" << endl;
        }
        return 0;
    }
    View Code

    G - Sudoku ZOJ - 3122

    这道题就只是四阶数独而已,只是将N改为了16。

    建模的思路与之前的3阶数独是一样的。就不讲了(注意这里有些OJ的题面给的样例是错的,可以使用我注释里的)

    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    using namespace std;
    typedef long long ll;
    
    const int N = 16;
    const int maxn = N*N*N + 10;
    const int maxnode=maxn*4+maxn+10;
    const int INF=0x3f3f3f3f;
    string g;
    
    
    struct DLX{
        #define FF(i,A,S) for(int i = A[S];i != S;i = A[i])
        int L[maxnode],R[maxnode],U[maxnode],D[maxnode];//每个点的左右上下指针,所在行列
        int sz,col[maxnode],row[maxnode],S[maxn],H[maxn];//H每行的头结点,S每列的节点数
        int ans[maxn],cnt;
        int out[maxn];
        int n,m;
    
        void init(int _n,int _m) {
            n=_n, m=_m;
            for(int i = 0;i <= m;i ++)
                S[i]=0,U[i]=D[i]=i,L[i]=i-1,R[i]=i+1;
            R[m] = 0, L[0] = sz = m;
            for(int i = 1;i <= n; ++i) H[i] = -1;
        }
    
        void link(int r,int c) {
            ++S[col[++sz]=c], row[sz] = r;
            D[sz] = D[c], U[D[c]] = sz;
            U[sz] = c, D[c] = sz;
            if(H[r] < 0) H[r] = L[sz] = R[sz] = sz;
            else R[sz]=R[H[r]], L[R[H[r]]]=sz,L[sz]= H[r],R[H[r]] = sz;
        }
        void del(int c){//精确覆盖,删除涉及C列的集合
            L[R[c]]=L[c],R[L[c]]=R[c];
            FF(i,D,c)FF(j,R,i)U[D[j]]=U[j],D[U[j]]=D[j],--S[col[j]];
        }
        void add(int c){  //精确覆盖,恢复涉及C列的集合
            R[L[c]]=L[R[c]]=c;
            FF(i,U,c)FF(j,L,i)++S[col[U[D[j]]=D[U[j]]=j]];
        }
        bool dance(int k){//精确覆盖
            //(剪枝)if(cnt != -1 && cnt <= d) return 0;//精确匹配输出最小
            if(!R[0]){
                //cnt=min(cnt,k);//精确匹配输出最小
                for(int i = 0;i < k; ++i)
                    out[(ans[i]-1)/16]=(ans[i]-1)%16+1;
                cnt=k;
                output();
                return 1;
            }
            int c=R[0];FF(i,R,0)if(S[c]>S[i])c=i;
            del(c);
            FF(i,D,c){
                FF(j,R,i)del(col[j]);
                ans[k]=row[i];
                if(dance(k+1))return 1;
                //dance(k+1);//精确匹配输出最小
                FF(j,L,i)add(col[j]);
            }
            add(c);
            return 0;
        }
        void output() {
            for (int i = 0; i < cnt; ++i) {
                cout << (char)(out[i]-1+'A');
                if ((i+1)%16==0) cout << endl;
            }
        }
    }dlx;
    
    int main() {
        string temp; int cnt = -1;
        while (cin >> temp) {
            g.clear(), g += temp;
            for (int i = 1; i < 16; ++i) cin >> temp, g += temp;
            dlx.init(N * N * N,  N * N * 4);
            for (int i = 0; i < N; ++i)
                for (int j = 0; j < N; ++j)
                    for(int k = 1; k <= N;++k) {
                        if(g[i * N+j] == '-' || g[i * N + j] == 'A'+k-1) {
                            dlx.link((i*N+j)*N+k, i*N+j+1);
                            dlx.link((i*N+j)*N+k, N*N+i*N+k);
                            dlx.link((i*N+j)*N+k, N*N*2+j*N+k);
                            dlx.link((i*N+j)*N+k, N*N*3+((i/4)*4+(j/4))*N+k);
                        }
                    }
            if (++cnt) cout << endl;
            dlx.dance(0);
        }
    
        return 0;
    }
    
    /*
    --A----C-----O-I
    -J--A-B-P-CGF-H-
    --D--F-I-E----P-
    -G-EL-H----M-J--
    ----E----C--G---
    -I--K-GA-B---E-J
    D-GP--J-F----A--
    -E---C-B--DP--O-
    E--F-M--D--L-K-A
    -C--------O-I-L-
    H-P-C--F-A--B---
    ---G-OD---J----H
    K---J----H-A-P-L
    --B--P--E--K--A-
    -H--B--K--FI-C--
    --F---C--D--H-N-
    */
    View Code

    H - Squiggly Sudoku HDU - 4069

    这道题倒是挺有趣的,难的地方在他的宫的构建(虽然也不难

    我的想法是先BFS预处理一遍每个的宫,并将其标号,并将宫里的格子用宫号标记

    之后就是常规数独的做法——LINK(行, 格子限制), LINK(行, 行限制), LINK(行, 列限制), LINK(行, 格子对应的宫号限制);

    之后就看我的代码吧。

    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <queue>
    using namespace std;
    typedef long long ll;
    
    const int N = 9;
    const int maxn = N*N*N + 10;
    const int maxnode=maxn*4+maxn+10;
    const int INF=0x3f3f3f3f;
    int g[N][N];
    int wall[N][N][4];//用于标记墙
    int id[N][N];   //用于划分宫
    int dic[4][2]={0,-1, 1,0, 0,1, -1,0};
    int gong_id;
    int res[N*N]; //用于最后输出
    
    struct DLX{
        #define FF(i,A,S) for(int i = A[S];i != S;i = A[i])
        int L[maxnode],R[maxnode],U[maxnode],D[maxnode];//每个点的左右上下指针,所在行列
        int sz,col[maxnode],row[maxnode],S[maxn],H[maxn];//H每行的头结点,S每列的节点数
        int ans[maxn],cnt;
        int out[maxn];
        int n,m;
    
        void init(int _n,int _m) {
            n=_n, m=_m, cnt=0;
            for(int i = 0;i <= m;i ++)
                S[i]=0,U[i]=D[i]=i,L[i]=i-1,R[i]=i+1;
            R[m] = 0, L[0] = sz = m;
            for(int i = 1;i <= n; ++i) H[i] = -1;
        }
    
        void link(int r,int c) {
            ++S[col[++sz]=c], row[sz] = r;
            D[sz] = D[c], U[D[c]] = sz;
            U[sz] = c, D[c] = sz;
            if(H[r] < 0) H[r] = L[sz] = R[sz] = sz;
            else R[sz]=R[H[r]], L[R[H[r]]]=sz,L[sz]= H[r],R[H[r]] = sz;
        }
        void del(int c){//精确覆盖,删除涉及C列的集合
            L[R[c]]=L[c],R[L[c]]=R[c];
            FF(i,D,c)FF(j,R,i)U[D[j]]=U[j],D[U[j]]=D[j],--S[col[j]];
        }
        void add(int c){  //精确覆盖,恢复涉及C列的集合
            R[L[c]]=L[R[c]]=c;
            FF(i,U,c)FF(j,L,i)++S[col[U[D[j]]=D[U[j]]=j]];
        }
        int dance(int k){//精确覆盖
            //(剪枝)if(cnt != -1 && cnt <= d) return 0;//精确匹配输出最小
            if(!R[0]){
                //cnt=min(cnt,k);//精确匹配输出最小
                for(int i = 0; i < k; ++i)
                    out[(ans[i]-1)/N]=(ans[i]-1)%N+1;
                ++cnt, memcpy(res, out, sizeof(res));
                if (cnt > 1) return -1;
            }
            int c=R[0];FF(i,R,0)if(S[c]>S[i])c=i;
            del(c);
            FF(i,D,c){
                FF(j,R,i)del(col[j]);
                ans[k]=row[i];
                if (dance(k+1)==-1) return -1;
                //dance(k+1);//精确匹配输出最小
                FF(j,L,i)add(col[j]);
            }
            add(c);
            if (cnt > 1) return -1;
            return 0;
        }
    }dlx;
    
    void BFS(int x, int y) {
        queue<pair<int, int> > q;
        q.push({x,y}), id[x][y] = ++gong_id;
        while (!q.empty()) {
            pair<int, int> top = q.front(); q.pop();
            for (int i = 0; i < 4; ++i) {
                int tx = top.first + dic[i][0];
                int ty = top.second + dic[i][1];
                if (wall[top.first][top.second][i] || id[tx][ty]) continue;
                id[tx][ty] = gong_id, q.push({tx,ty});
            }
        }
    }
    
    int main() {
        int T; cin >> T;
        for (int z = 1; z <= T; ++z) {
            for (int i = 0; i < N; ++i)
                for (int j = 0; j < N; ++j) {
                    cin >> g[i][j],
                    wall[i][j][0] = wall[i][j][1] = wall[i][j][2] = wall[i][j][3] = id[i][j] = 0;
                    if (g[i][j] >= 128) g[i][j] -= 128, wall[i][j][0]=1; //left
                    if (g[i][j] >= 64) g[i][j] -= 64, wall[i][j][1]=1;  //down
                    if (g[i][j] >= 32) g[i][j] -= 32, wall[i][j][2]=1;  //right
                    if (g[i][j] >= 16) g[i][j] -= 16, wall[i][j][3]=1;  //up
            }
    
            gong_id = 0;
            for (int i = 0; i < N; ++i)
                for (int j = 0; j < N; ++j)
                    if (!id[i][j]) BFS(i,j);
    
            dlx.init(N * N * N,  N * N * 4);
            for (int i = 0; i < N; ++i)
                for (int j = 0; j < N; ++j)
                    for(int k = 1; k <= N;++k) {
                        if(g[i][j] == 0 || g[i][j] == k) {
                            dlx.link((i*N+j)*N+k, i*N+j+1);
                            dlx.link((i*N+j)*N+k, N*N+i*N+k);
                            dlx.link((i*N+j)*N+k, N*N*2+j*N+k);
                            dlx.link((i*N+j)*N+k, N*N*3+(id[i][j]-1)*N+k);
                        }
                    }
            cout << "Case "<< z << ":" << endl;
            if (dlx.dance(0) == -1)  cout << "Multiple Solutions" << endl;
            else if (dlx.cnt)
                for (int i = 0; i < N*N; ++i) {
                    cout << res[i];
                    if ((i+1)%N==0) cout << endl;
                }
            else cout << "No solution" << endl;
        }
        return 0;
    }
    View Code

    之后是重复覆盖,这东西非常玄学。我有几次都会超时,有几次又不超。

    重复覆盖剪枝非常的重要,如果不剪枝就相当于是暴力回溯,这样的话他的解空间就会非常的大。必然会超时。

    但是说到剪枝,我所遇到的也就只是A()优化而已。

    D - 神龙的难题 FZU - 1686

    建模思路:

    行:(N-K+1)*(M-K+1) 所有可以被喷格子数

    列:所有的敌人数(因为所有的敌人都要被覆盖)

    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <queue>
    using namespace std;
    typedef long long ll;
    
    const int N = 9;
    const int maxn = N*N*N + 10;
    const int maxnode=maxn*4+maxn+10;
    const int INF=0x3f3f3f3f;
    int g[N][N];
    int wall[N][N][4];//用于标记墙
    int id[N][N];   //用于划分宫
    int dic[4][2]={0,-1, 1,0, 0,1, -1,0};
    int gong_id;
    int res[N*N]; //用于最后输出
    
    struct DLX{
        #define FF(i,A,S) for(int i = A[S];i != S;i = A[i])
        int L[maxnode],R[maxnode],U[maxnode],D[maxnode];//每个点的左右上下指针,所在行列
        int sz,col[maxnode],row[maxnode],S[maxn],H[maxn];//H每行的头结点,S每列的节点数
        int ans[maxn],cnt;
        int out[maxn];
        int n,m;
    
        void init(int _n,int _m) {
            n=_n, m=_m, cnt=0;
            for(int i = 0;i <= m;i ++)
                S[i]=0,U[i]=D[i]=i,L[i]=i-1,R[i]=i+1;
            R[m] = 0, L[0] = sz = m;
            for(int i = 1;i <= n; ++i) H[i] = -1;
        }
    
        void link(int r,int c) {
            ++S[col[++sz]=c], row[sz] = r;
            D[sz] = D[c], U[D[c]] = sz;
            U[sz] = c, D[c] = sz;
            if(H[r] < 0) H[r] = L[sz] = R[sz] = sz;
            else R[sz]=R[H[r]], L[R[H[r]]]=sz,L[sz]= H[r],R[H[r]] = sz;
        }
        void del(int c){//精确覆盖,删除涉及C列的集合
            L[R[c]]=L[c],R[L[c]]=R[c];
            FF(i,D,c)FF(j,R,i)U[D[j]]=U[j],D[U[j]]=D[j],--S[col[j]];
        }
        void add(int c){  //精确覆盖,恢复涉及C列的集合
            R[L[c]]=L[R[c]]=c;
            FF(i,U,c)FF(j,L,i)++S[col[U[D[j]]=D[U[j]]=j]];
        }
        int dance(int k){//精确覆盖
            //(剪枝)if(cnt != -1 && cnt <= d) return 0;//精确匹配输出最小
            if(!R[0]){
                //cnt=min(cnt,k);//精确匹配输出最小
                for(int i = 0; i < k; ++i)
                    out[(ans[i]-1)/N]=(ans[i]-1)%N+1;
                ++cnt, memcpy(res, out, sizeof(res));
                if (cnt > 1) return -1;
            }
            int c=R[0];FF(i,R,0)if(S[c]>S[i])c=i;
            del(c);
            FF(i,D,c){
                FF(j,R,i)del(col[j]);
                ans[k]=row[i];
                if (dance(k+1)==-1) return -1;
                //dance(k+1);//精确匹配输出最小
                FF(j,L,i)add(col[j]);
            }
            add(c);
            if (cnt > 1) return -1;
            return 0;
        }
    }dlx;
    
    void BFS(int x, int y) {
        queue<pair<int, int> > q;
        q.push({x,y}), id[x][y] = ++gong_id;
        while (!q.empty()) {
            pair<int, int> top = q.front(); q.pop();
            for (int i = 0; i < 4; ++i) {
                int tx = top.first + dic[i][0];
                int ty = top.second + dic[i][1];
                if (wall[top.first][top.second][i] || id[tx][ty]) continue;
                id[tx][ty] = gong_id, q.push({tx,ty});
            }
        }
    }
    
    int main() {
        int T; cin >> T;
        for (int z = 1; z <= T; ++z) {
            for (int i = 0; i < N; ++i)
                for (int j = 0; j < N; ++j) {
                    cin >> g[i][j],
                    wall[i][j][0] = wall[i][j][1] = wall[i][j][2] = wall[i][j][3] = id[i][j] = 0;
                    if (g[i][j] >= 128) g[i][j] -= 128, wall[i][j][0]=1; //left
                    if (g[i][j] >= 64) g[i][j] -= 64, wall[i][j][1]=1;  //down
                    if (g[i][j] >= 32) g[i][j] -= 32, wall[i][j][2]=1;  //right
                    if (g[i][j] >= 16) g[i][j] -= 16, wall[i][j][3]=1;  //up
            }
    
            gong_id = 0;
            for (int i = 0; i < N; ++i)
                for (int j = 0; j < N; ++j)
                    if (!id[i][j]) BFS(i,j);
    
            dlx.init(N * N * N,  N * N * 4);
            for (int i = 0; i < N; ++i)
                for (int j = 0; j < N; ++j)
                    for(int k = 1; k <= N;++k) {
                        if(g[i][j] == 0 || g[i][j] == k) {
                            dlx.link((i*N+j)*N+k, i*N+j+1);
                            dlx.link((i*N+j)*N+k, N*N+i*N+k);
                            dlx.link((i*N+j)*N+k, N*N*2+j*N+k);
                            dlx.link((i*N+j)*N+k, N*N*3+(id[i][j]-1)*N+k);
                        }
                    }
            cout << "Case "<< z << ":" << endl;
            if (dlx.dance(0) == -1)  cout << "Multiple Solutions" << endl;
            else if (dlx.cnt)
                for (int i = 0; i < N*N; ++i) {
                    cout << res[i];
                    if ((i+1)%N==0) cout << endl;
                }
            else cout << "No solution" << endl;
        }
        return 0;
    }
    View Code

    E - Square Destroyer POJ - 1084

    这道题目老恶心了。

    给每个规模下的方块标号。首先预处理每个规模下每根火柴对应的方块位置。

    (这块代码我是嫖网上的)

    建模思路:

    行:每根火柴(2*N*(N+1))

    列:所有的方块(N*N + (N-1)*(N-1) + ... 1*1)

    但是题目有可能会先拿走几根火柴。这部分就不是很好处理。

    网上有些是利用DLX内部的删除函数来删除的,但是我个人是喜欢用hash来写的。(其实就是不懂DLX的原理不会删)

    先将给出的火柴标记,并将火柴所对应的方块标记,之后把剩余的方块hash。(那么列数就是后面的hash数)

    后面通过hash来Link即可。

    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    #include <iomanip>
    using namespace std;
    typedef long long ll;
    
    const int N = 60 + 5;
    const int maxn = 1*1+2*2+3*3+4*4+5*5+5;
    const int maxnode=maxn*N+10;
    const int INF=0x3f3f3f3f;
    int n, m;
    
    int match[6][N][maxn];//矩阵大小,火柴标号,对应小框子, //第三级的0是用来记录影响了几个格子的
    bool isUsed[maxn];
    int hash_col[maxn];
    void init()
    {
        //总方格边长
        for(int len=1;len<=5;len++)  {
            int cnt=0;
            //小方格大小
            for(int sz=1;sz<=len;sz++)
                //起点横坐标
                for(int i=1;i<=len+1-sz;i++)
                //起点纵坐标
                    for(int j=0;j<len+1-sz;j++) {
                        ++cnt;
                        for(int k=0;k<sz;k++) match[len][i+j*(2*len+1)+k][++match[len][i+j*(2*len+1)+k][0]]=cnt;
                        for(int k=0;k<sz;k++) match[len][i+(j+sz)*(2*len+1)+k][++match[len][i+(j+sz)*(2*len+1)+k][0]]=cnt;
                        for(int k=0;k<sz;k++) match[len][i+len+(j+k)*(2*len+1)][++match[len][i+len+(j+k)*(2*len+1)][0]]=cnt;
                        for(int k=0;k<sz;k++) match[len][i+len+sz+(j+k)*(2*len+1)][++match[len][i+len+sz+(j+k)*(2*len+1)][0]]=cnt;
                    }
        }
    }
    
    struct DLX{
        #define FF(i,A,S) for(int i = A[S];i != S;i = A[i])
        int L[maxnode],R[maxnode],U[maxnode],D[maxnode];//每个点的左右上下指针,所在行列
        int sz,col[maxnode],row[maxnode],S[maxn],H[maxn];//H每行的头结点,S每列的节点数
        int ans[maxn],cnt;
        int out[maxn];
        int n,m;
    
        void init(int _n,int _m) {
            n=_n, m=_m;
            for(int i = 0;i <= m;i ++)
                S[i]=0,U[i]=D[i]=i,L[i]=i-1,R[i]=i+1;
            R[m] = 0, L[0] = sz = m;
            for(int i = 1;i <= n; ++i) H[i] = -1;
        }
    
        void link(int r,int c) {
            ++S[col[++sz]=c], row[sz] = r;
            D[sz] = D[c], U[D[c]] = sz;
            U[sz] = c, D[c] = sz;
            if(H[r] < 0) H[r] = L[sz] = R[sz] = sz;
            else R[sz]=R[H[r]], L[R[H[r]]]=sz,L[sz]= H[r],R[H[r]] = sz;
        }
    
        void remove(int c){//重复覆盖
            FF(i,D,c)L[R[i]]=L[i],R[L[i]]=R[i];
        }
         void resume(int c){//重复覆盖
             FF(i,U,c)L[R[i]]=R[L[i]]=i;
        }
        int A(){//估价函数
            int res=0;
            bool vis[m+1];
            memset(vis,0,sizeof(vis));
            FF(i,R,0)if(!vis[i]){
                    res++,vis[i]=1;
                    FF(j,D,i)FF(k,R,j)vis[col[k]]=1;
                }
            return res;
        }
        void dance(int now,int &ans){//重复覆盖,需要传入一个接收答案的ans
            if(R[0]==0) {
                ans=min(ans,now);
                return ;
            }
            else if(now+A()<ans){
                int temp=INF,c;
                FF(i,R,0)if(temp>S[i])temp=S[i],c=i;
                FF(i,D,c){
                    remove(i);
                    FF(j,R,i)remove(j);
                    dance(now+1,ans);
                    FF(j,L,i)resume(j);
                    resume(i);
                }
            }
        }
    }dlx;
    
    int main()
    {
        ios::sync_with_stdio(0);
        cin.tie(0);
        int T; cin >> T;
        init();
        while (T--) {
            cin >>  n >> m;
            memset(isUsed, 0, sizeof(isUsed));
            memset(hash_col, 0, sizeof(hash_col));
            for (int i = 0; i < m; ++i) {
                int temp; cin >> temp;
                for (int j=1; j <= match[n][temp][0]; ++j)
                    isUsed[match[n][temp][j]]=1;
            }
            int limit = 0; for (int i = 1; i <= n; ++i) limit += i*i;
            int col = 0;
            for (int i = 1; i <= limit; ++i) if (!isUsed[i]) hash_col[i]=++col;
            dlx.init(2*n*(n+1), col);
            for (int i = 1; i <= 2*n*(n+1); ++i)
                for (int j = 1; j <= match[n][i][0]; ++j)
                    if (hash_col[match[n][i][j]])
                        dlx.link(i, hash_col[match[n][i][j]]);
            int ans = INF;
            dlx.dance(0,ans);
            cout << ans << endl;
        }
        return 0;
    }
    View Code

    C - Radar HDU - 2295

    这道题非常的经典。二分答案+DLXcheck

    建模思路:

    行:雷达数量 M

    列:城市数量 N (每个城市都需要被覆盖)

    但是这道题还给了一个K操作员的条件,所以就可以放在A()里剪枝

    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    #include <iomanip>
    using namespace std;
    typedef long long ll;
    
    const int N = 50+5;
    const int maxn = N*N + 10;
    const int maxnode=maxn*N+10;
    const int INF=0x3f3f3f3f;
    bool g[N][N];
    int n, m, k;
    
    struct node {
        int x, y;
         bool operator < (const node& a) const {
            if (x == a.x) return y < a.y;
            return x < a.x;
        }
    }r[N],c[N];
    
    struct node1 {
        double val;
        int id;
        bool operator <(const node1& a) const{
            return val < a.val;
        }
    };
    vector<node1> dist[N];
    
    struct DLX{
        #define FF(i,A,S) for(int i = A[S];i != S;i = A[i])
        int L[maxnode],R[maxnode],U[maxnode],D[maxnode];//每个点的左右上下指针,所在行列
        int sz,col[maxnode],row[maxnode],S[maxn],H[maxn];//H每行的头结点,S每列的节点数
        int ans[maxn],cnt;
        int out[maxn];
        int n,m;
    
        void init(int _n,int _m) {
            n=_n, m=_m;
            for(int i = 0;i <= m;i ++)
                S[i]=0,U[i]=D[i]=i,L[i]=i-1,R[i]=i+1;
            R[m] = 0, L[0] = sz = m;
            for(int i = 1;i <= n; ++i) H[i] = -1;
        }
    
        void link(int r,int c) {
            ++S[col[++sz]=c], row[sz] = r;
            D[sz] = D[c], U[D[c]] = sz;
            U[sz] = c, D[c] = sz;
            if(H[r] < 0) H[r] = L[sz] = R[sz] = sz;
            else R[sz]=R[H[r]], L[R[H[r]]]=sz,L[sz]= H[r],R[H[r]] = sz;
        }
    
        void remove(int c){//重复覆盖
            FF(i,D,c)L[R[i]]=L[i],R[L[i]]=R[i];
        }
         void resume(int c){//重复覆盖
             FF(i,U,c)L[R[i]]=R[L[i]]=i;
        }
        int A(){//估价函数
            int res=0;
            bool vis[m+1];
            memset(vis,0,sizeof(vis));
            FF(i,R,0)if(!vis[i]){
                    res++,vis[i]=1;
                    FF(j,D,i)FF(k,R,j)vis[col[k]]=1;
                }
            return res;
        }
        void dance(int now,int &ans){//重复覆盖,需要传入一个接收答案的ans
            if(R[0]==0) {
                ans=min(ans,now);
                return ;
            }
            else if(now+A()> k) return ;
            else if(now+A()<ans){
                int temp=INF,c;
                FF(i,R,0)if(temp>S[i])temp=S[i],c=i;
                FF(i,D,c){
                    remove(i);
                    FF(j,R,i)remove(j);
                    dance(now+1,ans);
                    FF(j,L,i)resume(j);
                    resume(i);
                }
            }
        }
    }dlx;
    
    bool Link(double rad) {
        dlx.init(m, n);
        node1 temp = {rad, 0};
        for (int i = 0; i < m; ++i) {
            int limit = upper_bound(dist[i].begin(), dist[i].end(), temp)-dist[i].begin();
            for (int j = 0; j < limit; ++j)
                dlx.link(i+1, dist[i][j].id+1);
        }
        int ans = INF;
        dlx.dance(0, ans);
        return (ans==INF)?0:1;
    }
    
    double cal(double x1, double x2, double y1, double y2) {
        return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
    }
    
    int main()
    {
        int T; cin >> T;
        while (T--) {
            cin >>  n >> m >> k;
            for (int i = 0; i < n; ++i) cin >> c[i].x >> c[i].y;
            for (int i = 0; i < m; ++i) cin >> r[i].x >> r[i].y;
            for (int i = 0; i < m; ++i) {
                dist[i].clear();
                for (int j = 0; j < n; ++j) {
                    dist[i].push_back({cal(r[i].x,c[j].x,r[i].y,c[j].y), j});
                }
                sort(dist[i].begin(), dist[i].end());
    //            for (int j = 0; j < n; ++j) cout << " " << dist[i][j].id;
    //            cout << endl;
            }
    
            double L = 0, R = 1e4;
            while (R - L > 1e-8) {
                double MID = (R+L)/2;
                //cout << MID << endl;
                if (Link(MID)) R = MID;
                else L = MID;
            }
            cout << fixed << setprecision(6) << R << endl;
        }
        return 0;
    }
    View Code

    这道题跟上一道差不多。区别就是改成了曼哈顿距离

    建模思路:

    行:城市数量 N

    列:城市数量 N (每个城市都需要被覆盖)

    整数二分的姿势别错了

    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    #include <iomanip>
    using namespace std;
    typedef long long ll;
    
    const int N = 60+5;
    const int maxn = N*N + 10;
    const int maxnode=maxn*N+10;
    const int INF=0x3f3f3f3f;
    int n, k;
    
    struct node {
        ll x, y;
         bool operator < (const node& a) const {
            if (x == a.x) return y < a.y;
            return x < a.x;
        }
    }r[N],c[N];
    
    ll dist[N][N];
    
    struct DLX{
        #define FF(i,A,S) for(int i = A[S];i != S;i = A[i])
        int L[maxnode],R[maxnode],U[maxnode],D[maxnode];//每个点的左右上下指针,所在行列
        int sz,col[maxnode],row[maxnode],S[maxn],H[maxn];//H每行的头结点,S每列的节点数
        int ans[maxn],cnt;
        int out[maxn];
        int n,m;
    
        void init(int _n,int _m) {
            n=_n, m=_m;
            for(int i = 0;i <= m;i ++)
                S[i]=0,U[i]=D[i]=i,L[i]=i-1,R[i]=i+1;
            R[m] = 0, L[0] = sz = m;
            for(int i = 1;i <= n; ++i) H[i] = -1;
        }
    
        void link(int r,int c) {
            ++S[col[++sz]=c], row[sz] = r;
            D[sz] = D[c], U[D[c]] = sz;
            U[sz] = c, D[c] = sz;
            if(H[r] < 0) H[r] = L[sz] = R[sz] = sz;
            else R[sz]=R[H[r]], L[R[H[r]]]=sz,L[sz]= H[r],R[H[r]] = sz;
        }
    
        void remove(int c){//重复覆盖
            FF(i,D,c)L[R[i]]=L[i],R[L[i]]=R[i];
        }
         void resume(int c){//重复覆盖
             FF(i,U,c)L[R[i]]=R[L[i]]=i;
        }
        int A(){//估价函数
            int res=0;
            bool vis[m+1];
            memset(vis,0,sizeof(vis));
            FF(i,R,0)if(!vis[i]){
                    res++,vis[i]=1;
                    FF(j,D,i)FF(k,R,j)vis[col[k]]=1;
                }
            return res;
        }
        bool dance(int now){//重复覆盖,需要传入一个接收答案的ans
            if(R[0]==0) {
                return 1;
            }
            else if(now+A()<=k){
                int temp=INF,c;
                FF(i,R,0)if(temp>S[i])temp=S[i],c=i;
                FF(i,D,c){
                    remove(i);
                    FF(j,R,i)remove(j);
                    if (dance(now+1)) return 1;
                    FF(j,L,i)resume(j);
                    resume(i);
                }
            }
            return 0;
        }
    }dlx;
    
    ll cal(ll x1, ll x2, ll y1, ll y2) {
        return abs(x1-x2)+abs(y1-y2);
    }
    
    int main()
    {
        cin.tie(0);
        ios::sync_with_stdio(0);
        int T; cin >> T;
        for (int z = 1; z <= T; ++z) {
            cin >>  n >> k;
            for (int i = 0; i < n; ++i)
                cin >> c[i].x >> c[i].y;
    
            ll l = 0, r = 100000000000LL;
            ll ans = r;
            while (l <= r) {
                ll mid = (l + r) / 2;
                dlx.init(n, n);
                for (int i = 0; i < n; i++) {
                    for (int j = 0; j < n; j++) {
                        if (cal(c[i].x, c[j].x, c[i].y, c[j].y) <= mid)
                            dlx.link(i+1, j+1);
                    }
                }
                if (dlx.dance(0)) {
                    r = mid - 1;
                    ans = min(ans, mid);
                }
                else l = mid + 1;
            }
            cout << "Case #" << z << ": " << ans << endl;
        }
        return 0;
    }
    View Code

    DLX进阶技巧:

    一、用于求最大集合的数量,集合内的数两两满足一个条件

     I - Divisibility

    题目大意:求选出两两不能整除的最大个数

    解题思路:

    做法比较多,本人是在DLX专题里做到所以就用DLX来做了

    枚举每两个数,若能相除就 link i 跟 j 代表选择 i 不选择 j

    最后把最小重复覆盖改成最大就行了

    核心:对题目的图形灵活思考,

    这里的所有列被覆盖即代表所有数都不能被选,即两两相除互不为0的一种情况

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1000+5;
    const int maxn = 500010;
    const int maxnode=maxn+10;
    const int INF=0x3f3f3f3f;
    int n;
    
    struct DLX{
        #define FF(i,A,S) for(int i = A[S];i != S;i = A[i])
        int L[maxnode],R[maxnode],U[maxnode],D[maxnode];//每个点的左右上下指针,所在行列
        int sz,col[maxnode],row[maxnode],S[maxn],H[maxn];//H每行的头结点,S每列的节点数
        int ans[maxn],cnt;
        int out[maxn];
        int n,m;
    
        void init(int _n,int _m) {
            n=_n, m=_m;
            for(int i = 0;i <= m;i ++)
                S[i]=0,U[i]=D[i]=i,L[i]=i-1,R[i]=i+1;
            R[m] = 0, L[0] = sz = m;
            for(int i = 1;i <= n; ++i) H[i] = -1;
        }
    
        void link(int r,int c) {
            ++S[col[++sz]=c], row[sz] = r;
            D[sz] = D[c], U[D[c]] = sz;
            U[sz] = c, D[c] = sz;
            if(H[r] < 0) H[r] = L[sz] = R[sz] = sz;
            else R[sz]=R[H[r]], L[R[H[r]]]=sz,L[sz]= H[r],R[H[r]] = sz;
        }
        void del(int c){//精确覆盖,删除涉及C列的集合
            L[R[c]]=L[c],R[L[c]]=R[c];
            FF(i,D,c)FF(j,R,i)U[D[j]]=U[j],D[U[j]]=D[j],--S[col[j]];
        }
        void add(int c){  //精确覆盖,恢复涉及C列的集合
            R[L[c]]=L[R[c]]=c;
            FF(i,U,c)FF(j,L,i)++S[col[U[D[j]]=D[U[j]]=j]];
        }
        bool dance(int k){//精确覆盖
            //(剪枝)if(cnt != -1 && cnt <= d) return 0;//精确匹配输出最小
            if(!R[0]){
                //cnt=min(cnt,k);//精确匹配输出最小
                for(int i = 0;i < k; ++i)
                    out[(ans[i]-1)/N]=(ans[i]-1)%N+1; //数独输出
                cnt=k;
                return 1;
            }
            int c=R[0];FF(i,R,0)if(S[c]>S[i])c=i;
            del(c);
            FF(i,D,c){
                FF(j,R,i)del(col[j]);
                ans[k]=row[i];
                if(dance(k+1))return 1;
                //dance(k+1);//精确匹配输出最小
                FF(j,L,i)add(col[j]);
            }
            add(c);
            return 0;
        }
        void remove(int c){//重复覆盖
            FF(i,D,c)L[R[i]]=L[i],R[L[i]]=R[i];
        }
         void resume(int c){//重复覆盖
             FF(i,U,c)L[R[i]]=R[L[i]]=i;
        }
        int A(){//估价函数
            int res=0;
            bool vis[m+1];
            memset(vis,0,sizeof(vis));
            FF(i,R,0)if(!vis[i]){
                    res++,vis[i]=1;
                    FF(j,D,i)FF(k,R,j)vis[col[k]]=1;
                }
            return res;
        }
        void dance(int now,int &ans){
            if(R[0]==0)ans=max(ans,now); //改为max
            else if(now+A()>ans){
                int temp=INF,c;
                FF(i,R,0)if(temp>S[i])temp=S[i],c=i;
                FF(i,D,c){
                    remove(i);
                    FF(j,R,i)remove(j);
                    dance(now+1,ans);
                    FF(j,L,i)resume(j);
                    resume(i);
                }
            }
        }
    }dlx;
    
    
    int main()
    {
        ios::sync_with_stdio(0);
        cin.tie(0);
        ll num[N];
        int T;
        cin >> T;
        while (T--) {
            cin >> n;
            for (int i = 1; i <= n; ++ i) cin >> num[i];
            dlx.init(n,n);
            for (int i = 1; i <= n; ++ i)
                for (int j = 1; j <= n; ++ j)
                    if (num[i] % num[j] == 0 || num[j] % num[i] == 0)
                        dlx.link(i, j);//, cout << i << "  " << j << endl;
            int ans = 0;
            dlx.dance(0, ans);
            cout << ans << endl;
        }
    
        return 0;
    }
    View Code
  • 相关阅读:
    spring事务管理中,使用Synchronized修饰事务方法,同步为什么会失效
    java枚举类是怎么初始化的,为什么说枚举类是线程安全的
    spring事务管理中,同一个Service类中非事务方法调用事务方法,事务失效解决方法(3种)
    spring boot借助freemarker实现word文档模板导出
    mybatis plus使用报错:Caused by: java.sql.SQLException: sql injection violation, syntax error: syntax err
    js中遍历枚举类型
    IDEA 设置SVN提交忽略文件 target .iml
    python中的open( )函数
    使用crf++
    安装crf++
  • 原文地址:https://www.cnblogs.com/Vikyanite/p/13238160.html
Copyright © 2011-2022 走看看