zoukankan      html  css  js  c++  java
  • POJ 3076 Sudoku

    Sudoku
    Time Limit: 10000MS   Memory Limit: 65536K
    Total Submissions: 4628   Accepted: 2237

    Description

    A Sudoku grid is a 16x16 grid of cells grouped in sixteen 4x4 squares, where some cells are filled with letters from A to P (the first 16 capital letters of the English alphabet), as shown in figure 1a. The game is to fill all the empty grid cells with letters from A to P such that each letter from the grid occurs once only in the line, the column, and the 4x4 square it occupies. The initial content of the grid satisfies the constraints mentioned above and guarantees a unique solution.

    Write a Sudoku playing program that reads data sets from a text file.

    Input

    Each data set encodes a grid and contains 16 strings on 16 consecutive lines as shown in figure 2. The i-th string stands for the i-th line of the grid, is 16 characters long, and starts from the first position of the line. String characters are from the set {A,B,…,P,-}, where – (minus) designates empty grid cells. The data sets are separated by single empty lines and terminate with an end of file.

    Output

    The program prints the solution of the input encoded grids in the same format and order as used for input.

    Sample Input

    --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-

    Sample Output

    FPAHMJECNLBDKOGI
    OJMIANBDPKCGFLHE
    LNDKGFOIJEAHMBPC
    BGCELKHPOFIMAJDN
    MFHBELPOACKJGNID
    CILNKDGAHBMOPEFJ
    DOGPIHJMFNLECAKB
    JEKAFCNBGIDPLHOM
    EBOFPMIJDGHLNKCA
    NCJDHBAEKMOFIGLP
    HMPLCGKFIAENBDJO
    AKIGNODLBPJCEFMH
    KDEMJIFNCHGAOPBL
    GLBCDPMHEONKJIAF
    PHNOBALKMJFIDCEG
    IAFJOECGLDPBHMNK

    这个数独的题目用到的是之前一篇文章中用到的DLX算法,即将数独问题转化为精确覆盖问题。具体转化的方法是根据数独的特性来的,首先作为数独,它的约束条件有4个

    1.每一行每个数字只能填一遍

    2.每一列每个数字只能填一遍

    3.每个宫每个数字只能填一遍

    4.每个格子只能填一个数字。

    根据上面的规则,我们将数独问题转化为精确覆盖问题。矩阵构造如下:


    第1列定义为第一行填了数字1

    第2列定义为第一行填了数字2

    第16列定义为第一行填了数字16

    第17列定义为第二行填了数字1

    以此类推,第256列定义为第16行填了数字16

    这样,用1-256列完成了第一个约束条件,下面我们继续:


    第257列定义为第1列填了数字1

    第258列定义为第1列填了数字2

    第512列定义为第16列填了数字16

    这样,用257-512列完成了第二个约束条件



    第513列定义为在第一宫填了数字1

    第514列定义了在第一宫填了数字2

    第768列定义了在第十六宫填了数字16

    这样,513-768列完成了第三个约束条件


    第769列定义了(1,1)填了一个数字

    第770列定义了(1,2)填了一个数字

    在1024列定义了(16,16)填了一个数字

    这样,769-1024列完成了第四个约束条件


    举个例子:

    例如(6,4)这个点填了3,分别转化为矩阵中对应的列为:

    1.在第6行中填了数字3,对应列数为:(6 - 1) * 16 + 3

    2.在第4列中填了数字3,对应列数为:16 * 16  + (4 - 1) + 3

    3.在第5宫中填了数字3,对应列数为:16 * 16 * 2 + (5 - 1) * 16 + 3 (其中宫的算法是:palace = (i - 1) /4 * 4 + (j - 1) / 4 + 1)

    4.在(6,4)填了一个数字,对应列数为:16 * 16 * 3 + 84  (其中位置的算法是:pos = (i - 1) * 16 + j)


    这样,对应(6,4)这个填3对应矩阵的一行就是:上述的列数置为1,其余列都是0,将这一列加入到矩阵中去。


    至此所有约束条件完成了,矩阵构造完成了,实际上矩阵已然转化为精确覆盖问题,就可以使用之前转载的一篇博客中的DLX算法求解:http://blog.csdn.net/chilumanxi/article/details/48297149,具体见代码:

    /*************************************************************************
    	> File Name: Sudoku.cpp
    	> Author: Zhanghaoran0
    	> Mail: chiluamnxi@gmail.com
    	> Created Time: 2015年09月08日 星期二 20时52分43秒
     ************************************************************************/
    
    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    const int N = 5000;
    const int M = 700000;
    
    int U[M], D[M], L[M], R[M];
    int C[M];
    int H[N], S[N];
    int mark[M];
    int size, n, m, Suc[N], flag;
    int tnum[M];
    
    void Link(int a, int b){                                                
        S[b] ++;                                                            //对应列数结点数+1
        C[size] = b;                                                        //记录列数
        U[size] = U[b];                                                     //将上下的联系连接上
        D[U[b]] = size;
        D[size] = b;
        U[b] = size;
        if(H[a] == -1)                                                      //如果该行还没有加入过结点,指向自己
            H[a] = L[size] = R[size] = size;
        else{
            L[size] = L[H[a]];                                              //若已经加入过结点,则将此结点加入,将左右的连接关系加上。
            R[L[H[a]]] = size;
            R[size] = H[a];
            L[H[a]] = size;
        }
        mark[size] = a;                                                     
        size ++;
    }
    
    void Delete(int a){                                                     //删除结点
        L[R[a]] = L[a];
        R[L[a]] = R[a];
        for(int i = D[a]; i != a; i = D[i]){
            for(int j = R[i]; j != i; j = R[j]){
                U[D[j]] = U[j];
                D[U[j]] = D[j];
                S[C[j]] --;
            }
        }
    }
    
    
    void Resume(int a){
        for(int i = U[a]; i != a; i = U[i]){                                //只是改变了周围四个指针的数值,所以很简单就能恢复
            for(int j = L[i]; j != i; j = L[j]){
                U[D[j]] = j;
                D[U[j]] = j;
                S[C[j]] ++;
            }
        }
        L[R[a]] = a;
        R[L[a]] = a;
    }
    
    void Dance(int a){
        int Min = N;
        int t;
        if(!R[0]){                                                          //如果作为辅助的第一行没有任何结点的话就视为完成
            flag = 1;
            sort(Suc, Suc + a);
            for(int i = 0; i < a; i ++){
                cout << (char)(tnum[mark[Suc[i]]] + 'A' - 1);
                if((i + 1) % 16 == 0)
                    cout << endl;
            }
            cout << endl;
            return ;
        }
    
        for(int i = R[0]; i; i = R[i]){                                     //优先删除结点少的列数
            if(S[i] < Min){
                Min = S[i];
                t = i;
            }
        }
        Delete(t);
        for(int i = D[t]; i != t; i = D[i]){
            Suc[a] = i;                                                     //用这个数组来记录一行所有的字母
            for(int j = R[i]; j != i; j = R[j])
                Delete(C[j]);
            Dance(a + 1);
            if(flag)
                return ;
            for(int j = L[i]; j != i; j = L[j])
                Resume(C[j]);
        }
        Resume(t);                                                          //回溯
    }
    
    
    char Map[20][20];
    int Matrix[16 * 16 * 16 + 10][16 * 16 * 4 + 10];
    int main(void){
        while(scanf("%s", Map[0]) == 1){
            for(int i = 1; i < 16; i ++)
                scanf("%s", Map[i]);
            memset(Matrix, 0, sizeof(Matrix));
            m = 16 * 16 * 4;
            n = 0;
            for(int i = 1; i <= 16; i ++){
                for(int j = 1; j <= 16; j ++){
                    int row = i;
                    int col = j;
                    int palace = (i - 1) / 4 * 4 + (j - 1) / 4 + 1;         //宫的位置
                    int pos = (i - 1) * 16 + j;                             //实际位置
                    if(Map[i - 1][j - 1] == '-'){
                        for(int k = 1; k <= 16; k ++){                      //对于还没有存入数字的坐标,将所有情况写入
                            ++ n;
                            tnum[n] = k;                                    //记录存入数组的个数和对应的数字
                            Matrix[n][(row - 1) * 16 + k] = 1;              //以下四个条件分别对应行,列,宫,实际位置的条件
                            Matrix[n][16 * 16 + (col - 1) * 16 + k] = 1;
                            Matrix[n][16 * 16 * 2 + (palace - 1) * 16 + k] = 1;
                            Matrix[n][16 * 16 * 3 + pos] = 1;
                        }
                    }
                    else{
                        int k = Map[i - 1][j - 1] - 'A' + 1;                //对于实际存在的数字,同上写入
                        ++ n;
                        tnum[n] = k;
                        Matrix[n][(row - 1) * 16 + k] = 1;
                        Matrix[n][16 * 16 + (col - 1) * 16 + k] = 1;
                        Matrix[n][16 * 16 * 2 + (palace - 1) * 16 + k] = 1;
                        Matrix[n][16 * 16 * 3 + pos] = 1;
                    }
                }
            }
            
            for(int i = 0; i <= m; i ++){                                   //初始化所有的结点的连接关系,上下的关系没有连接所以先指向自己
                S[i] = 0;
                D[i] = U[i] = i;
                L[i + 1] = i;
                R[i] = i + 1;
    
            }
            R[m] = 0;                                                       //循环的性质
            size = m + 1;
            memset(H, - 1, sizeof(H));
            for(int i = 1; i <= n; i ++){
                for(int j = 1; j <= m; j ++){
                    if(Matrix[i][j])
                        Link(i, j);                                         //将所有已经存在的点连接
                }
            }
            flag = 0;
            Dance(0);
        }
        return 0;
    }





  • 相关阅读:
    tmp
    【ask】ghost分区还原win7出现蓝屏,试图加载CLASSPNP驱动时出现
    手动编译svn
    【ask】Recursive process.nextTick detected. This will break in the next version of node. Please use setImmediate for recursive deferral.
    c++11小计
    入门系列-ABP CLI
    入门系列-参数验证集成
    入门系列-异常处理
    .NET Core 控制台启动失败“以一种访问权限不允许的方式做了一个访问套接字的尝试”
    入门系列-虚拟文件系统
  • 原文地址:https://www.cnblogs.com/chilumanxi/p/5136085.html
Copyright © 2011-2022 走看看