码迷,mamicode.com
首页 > 其他好文 > 详细

LA 2659 && poj 3076 && zoj 3122 Sudoku(精确覆盖 + DLX)

时间:2014-11-17 21:18:14      阅读:264      评论:0      收藏:0      [点我收藏+]

标签:数据结构   递归   遍历   

题目链接: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;
}


LA 2659 && poj 3076 && zoj 3122 Sudoku(精确覆盖 + DLX)

标签:数据结构   递归   遍历   

原文地址:http://blog.csdn.net/lyhvoyage/article/details/41215367

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!