• Dance links X——随指尖而舞


    (Dance links X)

    (Dance) (links) (X)是一种高效的处理精准覆盖问题的算法

    常见精准覆盖问题包括但不限于:棋盘问题、数度问题、(k)皇后问题(话说用爆搜这不杀鸡一样?

    经典题目:八皇后、靶形数独、智(障)慧珠

    (DLX)算法是基于对(X)算法的优化,而(X)算法的本质是爆搜,(DLX)只不过是在(X)算法的基础上套用了一层神奇的数据结构——四向循环链表,以实现高效插入删除行。

    其主要思想是:
    将全集中的每个元素对应矩阵中的一个列,每个集合对应一个行。
    每次任选一个行,随即把该行和该行上有点的列和和包含这些列的行删除。
    每步选择之间用爆搜来转移 , 快速删除行列使用四向循环链表解决

    大致就是这样

    时空复杂度

    首先(Dance) (links) (X)的空间复杂度与该矩阵的(1)的数目有关

    时间复杂度为(O()玄学())
    但是在(P4929-DLX)模板中,时间复杂度大概为(O(c^n))

    (n)是矩阵 (1) 的个数,(c)是某个很接近(1(>1))的常数

    在座的各位应该都听过那个(1.01^{365})来描述努力成果的扯淡故事,显然在生活中它是不存在的,但在(Dance) (link) (X)中它确可能达到这个量级

    所以我们需要做好万全的准备

    (Optimization) (1)

    按照正常dfs剪枝的套路,每步选取最小的集合可以大大简化搜索树大小

    int co = r[0];
    for(int i=r[0];i!=0;i=r[i])
    {
        if(si[i] < si[co]) co= i; 
    }
    del(co);
    

    (Optimization) (2)

    这一步非常玄学,我也没大看明白为什么这样更优

    (back)操作从(delete)的反向来做(大致就是(delete)时用(r)指针右跳,(back)时用(l)指针左跳

    inline void back(int co)
    {
        for(int i=up[co] ; i!=co;i=up[i])
        {
            for(int j=l[i];j!=i;j = l[j])
            {
                dn[up[j]] = j;
                up[dn[j]] = j;
                ++si[col[j]];
            }
        }
    	r[l[co]] = co;
        l[r[co]] = co;    
    }
    
    for(int j=r[i] ; j!=i;j = r[j]) del(col[j]);
    if(dance(dep + 1) == 1) return 1;            
    for(int j=l[i];j!=i;j = l[j])back(col[j]);
    
    

    (Dance)——开始起舞

    随指尖而舞 
                ——元歌《王者荣耀》
    
        inline bool dance(int dep)  // dep表示深度
        {
            if(r[0]==0) //代表每个元素都已经被处理完美了
            {
            	print();
                return 1;
            }
            int co = r[0];
            for(int i=r[0];i!=0;i=r[i]) if(si[i] < si[co]) co= i; // Optimization_1
            
            del(co);//随便删一列
            for(int i=dn[co];i!=co;i = dn[i])//从该列中枚举要保留的行
            {
                int ro = row[i];
                Ans[dep] = ro;
                for(int j=r[i] ; j!=i;j = r[j]) del(col[j]);//表示这些列暂时已经处理好了
    			
                if(dance(dep + 1) == 1) return 1;
                
                for(int j=l[i];j!=i;j = l[j])back(col[j]);//Optimization_2
            }
            back(co);//回溯
            return 0;
        }
    

    (details)

    (insert)(delete)(back)按照链表正常写就行了也没啥(虽然因为这些东西调了很久

    (Code)

    #include<bits/stdc++.h>
    
    using namespace std;
    
    #define INF 1<<30
    #define int long long
    
    template<typename _T>
    inline void read(_T &x)
    {
        x= 0 ;int f =1;char s=getchar();
        while(s<'0' ||s>'9') {f =1;if(s == '-')f =- 1;s=getchar();}
        while('0'<=s&&s<='9'){x = (x<<3) + (x<<1) + s - '0';s= getchar();}
        x*=f;
    }
    
    struct Dance_links_X{
        int n,m,cnt;
        int Ans[23333];
        int up[23333],r[23333];
        int dn[23333],l[23333],head[23333];
        int row[233333],col[23333];
        int si[23333];
    
        inline void Init()
        {
            for(int i=0;i<=m;i++)
            {
                r[i] = i+1;
                l[i] = i-1;
                up[i] = dn[i] = i;
            }
            memset(head , -1, sizeof(head));
            r[m] = 0;
            l[0] = m;
            cnt = m;
        }
        inline void insert(int ro,int co)
        {
            col[++cnt] = co;
            ++si[co];
            row[cnt] = ro;
            up[cnt] = co;
            dn[cnt] = dn[co];
            up[dn[co]] = cnt;
            dn[co] = cnt;
            if(-1 != head[ro])
            {
                l[cnt] = head[ro];
                r[cnt] = r[head[ro]];
    			l[r[head[ro]]] = cnt;
                r[head[ro]] = cnt;
                
            }
            else head[ro] = l[cnt] = r[cnt] = cnt;
        }
    
        inline void del(int co)
        {
            r[l[co]] = r[co];
            l[r[co]] = l[co];
            for(int i=dn[co] ; i!=co;i=dn[i])
            {
                for(int j=r[i];j!=i;j = r[j])
                {
                    dn[up[j]] = dn[j];
                    up[dn[j]] = up[j];
                    --si[col[j]];
                }
            }
        }
    
        inline void back(int co)
        {
    
            for(int i=up[co] ; i!=co;i=up[i])
            {
                for(int j=l[i];j!=i;j = l[j])
                {
                    dn[up[j]] = j;
                    up[dn[j]] = j;
                    ++si[col[j]];
                }
            }
    		r[l[co]] = co;
            l[r[co]] = co;    
        }
    	
    	inline void print()
    	{
        	int i= -1;
        	while(Ans[i+1])
        	{
        		printf("%lld ",Ans[++i]);
    		}		
    	 } 
    
        inline bool dance(int dep)
        {
            if(r[0]==0)
            {
            	print();
                return 1;
            }
            int co = r[0];
            for(int i=r[0];i!=0;i=r[i]) if(si[i] < si[co]) co= i; 
            
            del(co);
            for(int i=dn[co];i!=co;i = dn[i])
            {
                int ro = row[i];
                Ans[dep] = ro;
                for(int j=r[i] ; j!=i;j = r[j]) del(col[j]);
    			
                if(dance(dep + 1) == 1) return 1;
                
                for(int j=l[i];j!=i;j = l[j])back(col[j]);
            }
            back(co);
            return 0;
        }
    
    }dlx;
    
    signed main()
    {
        int x,y;
        read(x);
        read(y);
        dlx.n = x;
        dlx.m = y;
        dlx.Init();
        for(int i=1;i<=x;i++)
        {
            for(int j=1,vl;j<=y;j++)
            {
                read(vl);
                if(vl) dlx.insert(i,j);
            }
        }
        if(!dlx.dance(0)) printf("No Solution!");
    }
    

    (End)

    Dance_links_X作为一种冷门算法在考场上遇到的可能性不大,就算遇到了也可以用爆搜剪枝剪过去。

    但其中所用到的数据结构——四向循环链表,其思想与特点需要认真理解,很可能在一些莫名其妙的构造题或是思维性数据结构题中遇到,显然是有掌握的必要的。

    在处理数独问题中有着巨大优势,其使用核心就是将数独问题转化为精确覆盖问题

  • 相关阅读:
    LevelDB
    Erlang的gen_server的terminate()/2未执行
    集群间心跳检测
    AI从业者关于数学的一些小建议
    《百面机器学习》拾贝----第三章:经典算法
    Demo——Image classification
    《百面机器学习》拾贝----第二章:模型评估
    《百面机器学习》拾贝----第一章:特征工程
    算法工程师---职业生涯规划
    License Plate Detection and Recognition in Unconstrained Scenarios(无约束场景下的车牌检测与识别)
  • 原文地址:https://www.cnblogs.com/-Iris-/p/15350335.html
Copyright © 2020-2023  润新知