LA 2659 && poj 3076 && zoj 3122 Sudoku(精確覆蓋 + DLX)

題目鏈接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=660

TimeLimit: 3.000 seconds

A Sudoku grid is a 16 x 16 grid of cells grouped in sixteen x 4 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 x 4 square it occupies. The initial content of the grid satisfies the constraints mentioned above and guarantees a unique solution.

Figure 1. Sudoku


    A         C           O   I
  J     A   B   P   C G F   H  
    D     F   I   E         P  
  G   E L   H         M   J    
        E         C     G      
  I     K   G A   B       E   J
D   G P     J   F         A    
  E       C   B     D P     O  
E     F   M     D     L   K   A
  C                 O   I   L  
H   P   C     F   A     B      
      G   O D       J         H
K       J         H   A   P   L
    B     P     E     K     A  
  H     B     K     F I   C    
    F       C     D     H   N  
                               

a) Sudoku grid


F P A H M J E C N L B D K O G I
O J M I A N B D P K C G F L H E
L N D K G F O I J E A H M B P C
B G C E L K H P O F I M A J D N
M F H B E L P O A C K J G N I D
C I L N K D G A H B M O P E F J
D O G P I H J M F N L E C A K B
J E K A F C N B G I D P L H O M
E B O F P M I J D G H L N K C A
N C J D H B A E K M O F I G L P
H M P L C G K F I A E N B D J O
A K I G N O D L B P J C E F M H
K D E M J I F N C H G A O P B L
G L B C D P M H E O N K J I A F
P H N O B A L K M J F I D C E G
I A F J O E C G L D P B H M N K
                               

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


題意:求解16行16列數獨問題的一組解:給定一個16*16的字母方陣,要求將所有的空格子填上A~P中的一個字符,並滿足以下條件。

1.每行A~P恰好各出現一次

2.每列A~P恰好各出現一次

3.粗線分隔出的每個4*4子方陣(一共有4*4個)中,A~P恰好各出現一次。

分析:直接套DLX

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;

const int maxn = 1024 + 10;
const int maxr = 4096 + 10;
const int maxnode = maxr * 4 + maxn;

// 行編號從1開始,列編號爲1~n,結點0是表頭結點,節點1~n是各列頂部的虛擬結點
struct DLX {
    int n, sz; // 列數,結點總數
    int S[maxn]; // 各列結點數

    int row[maxnode], col[maxnode]; // 各結點行列編號
    int L[maxnode], R[maxnode], U[maxnode], D[maxnode]; // 十字鏈表

    int ansd, ans[maxr]; // 解

    void init(int n) { // n是列數
        this->n = n;

        // 虛擬結點
        for(int i = 0; i <= n; i++) {
            U[i] = i; D[i] = i; L[i] = i - 1; R[i] = i + 1;
        }
        R[n] = 0; L[0] = n;

        sz = n + 1;
        memset(S, 0, sizeof(S));
    }

    void addRow(int r, vector<int> columns) {
        int first = sz;
        for(int i = 0; i < columns.size(); i++) {
            int c = columns[i];
            L[sz] = sz - 1; R[sz] = sz + 1; D[sz] = c; U[sz] = U[c];
            D[U[c]] = sz; U[c] = sz;
            row[sz] = r; col[sz] = c;
            S[c]++; sz++;
        }
        R[sz-1] = first; L[first] = sz - 1;
    }

    // 順着鏈表,遍歷除s外的其他元素  #define FOR(i, A, s) for(int i = A[s]; i != s; i = A[i])
    void remove(int c) {
        L[R[c]] = L[c];
        R[L[c]] = R[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]];
            }
    }

    void restore(int c) {
        for(int i = U[c]; i != c; i = U[i])
            for(int j = L[i]; j != i; j = L[j]) {
                ++S[col[j]];
                U[D[j]] = j;
                D[U[j]] = j;
            }
        L[R[c]] = c;
        R[L[c]] = c;
    }

    // d爲遞歸深度
    bool dfs(int d) {
        if(R[0] == 0) { // 找到解
            ansd = d; // 記錄解的長度
            return true;
        }
        int c = R[0]; //第一個未刪除的列
        for(int i = R[0]; i != 0; i = R[i])
            if(S[i] < S[c]) c = i;

        remove(c); // 刪除第c列
        for(int i = D[c]; i != c; i = D[i]) { // 用結點i所在行覆蓋第c列
            ans[d] = row[i];
            for(int j = R[i]; j != i; j = R[j]) remove(col[j]); // 刪除結點i所在行能覆蓋的所有其他列
            if(dfs(d + 1)) return true;
            for(int j = L[i]; j != i; j = L[j]) restore(col[j]); // 恢復結點i所在行能覆蓋的所有其他列
        }

        restore(c); // 恢復第c列
        return false;
    }

    bool solve(vector<int> &v) {
        v.clear();
        if(!dfs(0)) return false;
        for(int i = 0; i < ansd; i++) v.push_back(ans[i]);
        return true;
    }
};

DLX solver;

const int SLOT = 0;
const int ROW = 1;
const int COL = 2;
const int SUB = 3;

// 行/列的統一編解碼函數,從1開始編號
inline int encode(int a, int b, int c) {
    return a * 256 + b * 16 + c + 1;
}

void decode(int code, int &a, int &b, int &c) {
    code--;
    c = code % 16; code /= 16;
    b = code % 16; code /= 16;
    a = code;
}

char  puzzle[16][20];

bool read() {
    for(int i = 0; i < 16; i++)
        if(scanf("%s", puzzle[i]) == EOF) return false;
    return true;
}

int main()
{
    int kase = 0;
    while(read()) {
        if(++kase != 1) printf("\n");
        solver.init(1024);
        for(int r = 0; r < 16; r++)
            for(int c = 0; c < 16; c++)
                for(int v = 0; v < 16; v++)
                    if(puzzle[r][c] == '-' || puzzle[r][c] == 'A' + v) {
                        vector<int> columns;
                        columns.push_back(encode(SLOT, r, c));
                        columns.push_back(encode(ROW, r, v));
                        columns.push_back(encode(COL, c, v));
                        columns.push_back(encode(SUB, (r/4)*4+c/4, v));
                        solver.addRow(encode(r, c, v), columns);
                    }
        vector<int> ans;
        solver.solve(ans);

        for(int i = 0; i < ans.size(); i++) {
            int r, c, v;
            decode(ans[i], r, c, v);
            puzzle[r][c] = 'A' + v;
        }
        for(int i = 0; i < 16; i++)
            printf("%s\n", puzzle[i]);
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章