zoukankan      html  css  js  c++  java
  • Dancing Link --- 模板题 HUST 1017

     1017 - Exact cover

    Problem's Link:   http://acm.hust.edu.cn/problem/show/1017


    Mean: 

    给定一个由0-1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1

    analyse:

    初学DLX。

    这是DLX处理的最简单的问题,也是模板题。

    Time complexity: O(n*d)

    Source code: 

    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <queue>
    #include <set>
    #include <map>
    #include <string>
    #include <math.h>
    #include <stdlib.h>
    #include <time.h>
    using namespace std;
    const int MAXNode = 100010;
    const int MAXN = 1010;
    struct DLX
    {
        int n,m,size;
        int U[MAXNode],D[MAXNode],R[MAXNode],L[MAXNode],Row[MAXNode],Col[MAXNode];
        int H[MAXN], S[MAXN]; // H[i]---第i行第一个为1的index     S[i]---第i列为1的个数
        int ansd, ans[MAXN];
        void init(int _n,int _m)
        {
            n = _n;
            m = _m;
            for(int i = 0;i <= m;i++)  //  初始化第一行(图中的C[])
            {
                S[i] = 0; // 第i列为1的个数
                U[i] = D[i] = i;  
                L[i] = i-1;
                R[i] = i+1;
            }
            R[m] = 0; L[0] = m; // 第一行的最后一个指向第一行的第一个(成环)
            size = m;  // 从m开始以后的都是普通结点
            for(int i = 1;i <= n;i++)
                H[i] = -1;  // H[i]---第i行第一个为1的结点编号
        }
        void Link(int r,int c) // 行  列
        {
            // D[c] --- 第c列的下指针
            S[Col[++size]=c]++; // 普通结点下标++  第size个结点的列数是c    第c列的结点个数++
            Row[size] = r;     // 第size个结点的行数是r
            D[size] = D[c];   // 第size个结点的下指针是:第0行第c列的下指针
            U[size] = c;     // 第size个结点的上指针是:第0行第c列 (只有输入行是递增时才可以这样)
            U[D[c]] = size; // 第0行第c列的上指针是:size
            D[c] = size;   // size上面那个的下指针是:size (有点绕)
            if(H[r] < 0) H[r] = L[size] = R[size] = size; // 该行只有一个结点  左右指针自己指向自己
            else
            {
                R[size] = R[H[r]];  // 成环
                L[R[H[r]]] = size;
                L[size] = H[r];
                R[H[r]] = size;
            }
        }
        void remove(int c)  // 删除列c及其所在的行
        {
            L[R[c]] = L[c]; R[L[c]] = R[c]; // 左右两个结点连接,屏蔽掉c结点
            for(int i = D[c];i != c;i = D[i])  // 屏蔽掉所在的列
                for(int j = R[i];j != i;j = R[j])
                {
                    U[D[j]] = U[j];
                    D[U[j]] = D[j];
                    --S[Col[j]]; // j所在的列的数目减少
                }
        }
        void resume(int c)  //恢复列c缩对应的行
        {
            for(int i = U[c];i != c;i = U[i])
                for(int j = L[i];j != i;j = L[j])
                    ++S[Col[U[D[j]]=D[U[j]]=j]];
            L[R[c]] = R[L[c]] = c;
        }
        //d为递归深度
        bool Dance(int d)
        {
            if(R[0] == 0) // R[0]==R[m] // 第0行已经没有结点
            {
                ansd = d;
                return true;
            }
            int c = R[0];
            for(int i = R[0];i != 0;i = R[i]) // 往右走  ( 找出结点数最少的一列)
                if(S[i] < S[c])  //第i列结点个数 < 第c列结点个数
                    c = i;
            remove(c); // 移除列c所对应的行
            for(int i = D[c];i != c;i = D[i])  // 找到最小的这一列往下走
            {
                ans[d] = Row[i];
                for(int j = R[i]; j != i;j = R[j]) remove(Col[j]);  // 移除该行所对应的列
                if(Dance(d+1))return true;//递归下一层
                for(int j = L[i]; j != i;j = L[j])resume(Col[j]);//倒着恢复
            }
            resume(c);
            return false;
        }
    };
    
    DLX g;
    int main()
    {
        //freopen("in.txt","r",stdin);
        //freopen("out.txt","w",stdout);
        int n,m;
        while(scanf("%d%d",&n,&m) == 2)
        {
            g.init(n,m);
            for(int i = 1;i <= n;i++) //
            {
                int num,j;
                scanf("%d",&num);
                while(num--)
                {
                    scanf("%d",&j);  //
                    g.Link(i,j);
                }
            }
            if(!g.Dance(0)) printf("NO
    ");
            else
            {
                printf("%d",g.ansd);
                for(int i = 0;i < g.ansd;i++)
                    printf(" %d",g.ans[i]);
                printf("
    ");
            }
        }
        return 0;
    }
    View Code

    这个博客讲得非常细:

    http://www.cnblogs.com/grenet/p/3145800.html

     

  • 相关阅读:
    java算法:树遍历
    java算法:图遍历(深度优先和广度优先)
    Google禁止继续研发开源的"盖亚计划"
    Vc编程调试入门
    访著名Linux内核程序员大鹰
    访著名Linux内核程序员大鹰
    百度玩"精准搜索" 个人隐私保护问题值得商榷
    Google禁止继续研发开源的"盖亚计划"
    加密CMD使电脑溢出也拿不到CMD权限
    百度玩"精准搜索" 个人隐私保护问题值得商榷
  • 原文地址:https://www.cnblogs.com/crazyacking/p/4427300.html
Copyright © 2011-2022 走看看