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的剪枝效果真是让人惊叹……

你可能感兴趣的:(sudo)