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
  • 相关阅读:
    According to TLD or attribute directive in tag file, attribute end does not accept any expressions
    Several ports (8080, 8009) required by Tomcat v6.0 Server at localhost are already in use.
    sql注入漏洞
    Servlet—简单的管理系统
    ServletContext与网站计数器
    VS2010+ICE3.5运行官方demo报错----std::bad_alloc
    java 使用相对路径读取文件
    shell编程 if 注意事项
    Ubuntu12.04下eclipse提示框黑色背景色的修改方法
    解决Ubuntu环境变量错误导致无法正常登录
  • 原文地址:https://www.cnblogs.com/Vikyanite/p/13238160.html
Copyright © 2011-2022 走看看