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

a) Sudoku grid


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

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


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值