zoukankan      html  css  js  c++  java
  • HDU 3111 Sudoku ( Dancing Links 精确覆盖模型 )

    推荐两篇学DLX的博文:

    http://bbs.9ria.com/thread-130295-1-1.html(这篇对DLX的工作过程演示的很详细)

    http://yzmduncan.iteye.com/blog/1151695(这篇对精确覆盖与重复覆盖解释的简洁清晰,模板来自这篇博文)

    以下转载:

    DLX解决9*9的数独问题,转化为729*324的精确覆盖问题 

    行:一共9 * 9 * 9 == 729行。一共9 * 9小格,每一格有9种可能性(1 - 9),每一种可能都对应着一行。 

    列: 一共(9 + 9 + 9) * 9 + 81 == 324 种前面三个9分别代表着9行9列和9小块,乘以9的意思是9种可能(1 - 9),因为每种可能只可以选择一个。 81代表着81个小格,限制着每一个小格只放一个数字。 

    读入数据后,如果为'.',则建9行,即有1-9种可能,否则建一行,表示某小格只能放确定的某个数字。 

    以下个人理解:

    列: 一共(9 + 9 + 9) * 9 + 81 == 324 种

    对于这个(9+9+9)*9 我是这么理解的:一个数a,它在某一行有9个位置可以放,在某一列有9个位置可以放,在某一个3×3小格中有9个位置可以放,所以一共可以放的位置是(9+9+9)个,然后一共9个数字,所以是(9+9+9)*9 。

    不知道这样理解是否正确,望大家指教!

    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <algorithm>
    
    using namespace std;
    
    const int INF = 1 << 30;
    const int SZ = 9;
    const int MAXR = SZ * SZ * SZ;
    const int MAXC = ( SZ+SZ+SZ )*SZ + SZ*SZ;
    
    char mat[SZ+10][SZ+10];
    char str[SZ+10];
    bool maxtri[MAXR+40][MAXC+40];    //01矩阵
    int C[(MAXR+40)*(MAXC+40)], cnt[MAXC+40];
    int U[(MAXR+40)*(MAXC+40)], D[(MAXR+40)*(MAXC+40)];
    int L[(MAXR+40)*(MAXC+40)], R[(MAXR+40)*(MAXC+40)];
    int head;
    int ans[MAXR+40];
    int val[SZ+10][SZ+10];
    
    void Remove( int c )
    {
        int i, j;
        L[ R[c] ] = L[c];
        R[ L[c] ] = R[c];
        for ( i = D[c]; i != c; i = D[i] )
        {
            for ( j = R[i]; j != i; j = R[j] )
            {
                U[ D[j] ] = U[j];
                D[ U[j] ] = D[j];
                --cnt[ C[j] ];
            }
        }
        return;
    }
    
    void Resume( int c )
    {
        int i, j;
        R[ L[c] ] = c;
        L[ R[c] ] = c;
        for ( i = D[c]; i != c; i = D[i] )
        {
            for ( j = R[i]; j != i; j = R[j] )
            {
                U[ D[j] ] = j;
                D[ U[j] ] = j;
                ++cnt[ C[j] ];
            }
        }
        return;
    }
    
    bool DFS( int cur )
    {
        int i, j, c, minv;
        if ( R[head] == head )
            return true;
    
        minv = INF;
        for ( i = R[head]; i != head; i = R[i] )
        {
            if ( cnt[i] < minv )
            {
                minv = cnt[i];
                c = i;
            }
        }
    
        Remove(c);
        for ( i = D[c]; i != c; i = D[i] )
        {
            ans[cur] = (i - 1)/MAXC;
            for( j = R[i]; j != i; j = R[j] )
                Remove( C[j] );
    
            if ( DFS( cur + 1 ) ) return true;
    
            for( j = R[i]; j != i; j = R[j] )
                Resume( C[j] );
        }
    
        Resume(c);
        return false;
    }
    
    bool build()
    {
        int i, j, cur, pre, first;
        head = 0;
        for ( i = 0; i < MAXC; ++i )
        {
            R[i] = i + 1;
            L[i + 1] = i;
        }
        R[ MAXC ] = 0;
        L[0] = MAXC;
    
        //列双向链表
        for ( j = 1; j <= MAXC; ++j )
        {
            pre = j;
            cnt[j] = 0;
            for ( i = 1; i <= MAXR; ++i )
            {
                if ( maxtri[i][j] )
                {
                    ++cnt[j];
                    cur = i * MAXC + j;  //当前节点的编号
                    C[cur] = j;          //当前节点所在列
                    D[pre] = cur;
                    U[cur] = pre;
                    pre = cur;
                }
            }
            U[j] = pre;
            D[pre] = j;
            if ( !cnt[j] ) return false; //一定无解
        }
    
        //行双向链表
        for ( i = 1; i <= MAXR; ++i )
        {
            pre = first = -1;
            for ( j = 1; j <= MAXC; ++j )
            {
                if( maxtri[i][j] )
                {
                    cur = i * MAXC + j;
                    if ( pre == -1 ) first = cur;
                    else
                    {
                        R[pre] = cur;
                        L[cur] = pre;
                    }
                    pre = cur;
                }
            }
            if ( first != -1 )
            {
                R[pre] = first;
                L[first] = pre;
            }
        }
        return true;
    }
    
    /**************以上DLX模板*****************/
    
    void show()
    {
        for ( int i = 0; i <= MAXR; ++i )
        {
            for ( int j = 0; j <= MAXC; ++j )
                printf( "%d", maxtri[i][j] );
            puts("");
        }
        return;
    }
    
    //得到该情况下的01矩阵
    void init()
    {
        memset( maxtri, false, sizeof(maxtri) );
    
        for ( int i = 1; i <= SZ; ++i )
        {
            for ( int j = 1; j <= SZ; ++j )
            {
                int col = ( i - 1 ) * SZ + j;  //格子编号(1-81)
                if ( mat[i][j] == '?' )
                {
                    for ( int k = 1; k <= SZ; ++k )
                    {
                        maxtri[ (col - 1)*SZ + k ][col] = true;      //81保证不重复
                        maxtri[ (col - 1)*SZ + k ][ 81 + (i - 1)*SZ + k ] = true;    //9行中哪一行
                        maxtri[ (col - 1)*SZ + k ][ 162 + (j - 1)*SZ + k ] = true;   //9列中哪一列
                        maxtri[ (col - 1)*SZ + k ][ 243 + ((i-1)/3*3 + (j-1)/3 )*SZ + k ] = true;//9小格中哪一个小格
                    }
                }
                else
                {
                    int k = mat[i][j] - '0';
                    maxtri[ (col - 1)*SZ + k ][col] = true;
                    maxtri[ (col - 1)*SZ + k ][ 81 + (i - 1)*SZ + k ] = true;
                    maxtri[ (col - 1)*SZ + k ][ 162 + (j - 1)*SZ + k ] = true;
                    maxtri[ (col - 1)*SZ + k ][ 243 + ((i-1)/3*3 + (j-1)/3 )*SZ + k ] = true;
                }
            }
        }
        //show();
    
        return;
    }
    
    void PrintAns()
    {
        for ( int i = 0; i < 81; ++i )
        {
            int num = ans[i];
            int gird = num / SZ;
            if ( num % SZ ) ++gird;
            int aaa = num % SZ;
            if ( aaa == 0 ) aaa = 9;
            int x = (gird-1)/SZ+1;
            int y = (gird-1)%SZ+1;
            val[x][y] = aaa;
        }
    
        for ( int i = 1; i <= SZ; ++i )
        {
            for ( int j = 1; j <= SZ; ++j )
                printf( "%d", val[i][j] );
            puts("");
        }
        return;
    }
    
    int main()
    {
        //freopen( "in.txt", "r", stdin );
        //freopen( "out.txt", "w", stdout );
        int T;
        scanf( "%d", &T );
        while ( T-- )
        {
            for ( int i = 1; i <= SZ; ++i )
                scanf( "%s", &mat[i][1] );
            if ( T ) scanf( "%s", str );
            init();
            if ( build() )
            {
                if ( DFS(0) )
                    PrintAns();
                else puts("impossible");
            }
            else puts("impossible");
            if ( T ) puts("---");
        }
        return 0;
    }

    昨天比赛做到一个题,正解DLX,不过让薛薛位运算+剪枝直接暴过去了……跪。

    为了保险起见,今天学了一下DLX。目前只明白了精确覆盖,对重复覆盖还不是很了解。

    那个题似乎不是个很简单的DLX,需要精确覆盖+重复覆盖。orz,再接再厉吧。

    不得不说,DLX的剪枝效果真是让人惊叹……

  • 相关阅读:
    面向对象的七大设计原则
    06章 初始继承和多态
    面向太阳,不问春暖花开
    05章 体检套餐管理系统
    02章《深入C#数据类型》项目经理评分
    MongoDB快速入门(十二) -- 索引
    MongoDB快速入门(十一)- sort() 方法
    MongoDB快速入门(十)- Limit(),Skip() 方法
    MongoDB快速入门(九)- 投影
    MongoDB快速入门(八)- 删除文档
  • 原文地址:https://www.cnblogs.com/GBRgbr/p/3264986.html
Copyright © 2011-2022 走看看