您的位置:首页 > 理论基础 > 数据结构算法

POJ 3074 Sudoku(数据结构,DLX)

2014-06-27 14:52 417 查看
http://poj.org/problem?id=3074

Sudoku

Time Limit: 1000MSMemory Limit: 65536K
Total Submissions: 8141Accepted: 2854
Description

In the game of Sudoku, you are given a large 9 × 9 grid divided into smaller 3 × 3 subgrids. For example,

.2738..1.
.1...6735
.......29
3.5692.8.
.........
.6.1745.3
64.......
9518...7.
.8..6534.
Given some of the numbers in the grid, your goal is to determine the remaining numbers such that the numbers 1 through 9 appear exactly once in (1) each of nine 3 × 3 subgrids, (2) each of the nine rows, and (3) each of the nine columns.

Input

The input test file will contain multiple cases. Each test case consists of a single line containing 81 characters, which represent the 81 squares of the Sudoku grid, given one row at a time. Each character is either a digit (from 1 to 9) or a period (used
to indicate an unfilled square). You may assume that each puzzle in the input will have exactly one solution. The end-of-file is denoted by a single line containing the word “end”.

Output

For each test case, print a line representing the completed Sudoku puzzle.

Sample Input
.2738..1..1...6735.......293.5692.8...........6.1745.364.......9518...7..8..6534.
......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3.
end

Sample Output
527389416819426735436751829375692184194538267268174593643217958951843672782965341
416837529982465371735129468571298643293746185864351297647913852359682714128574936

Source

Stanford Local 2006
数独求解,用一般的搜索要超时,从中间开始搜大概900+ms过。优雅的求解数独(精确覆盖问题)需要用到Dancing Links这个非常高大上的数据结构,《算法竞赛入门经典——训练指南》(刘汝佳)中有对DLX(Dancing Links X算法)的介绍。

DLX模板:

/article/2059866.html

DLX本身并不复杂,写起来可能有点小麻烦,但是求解这类问题最难的是构造出01矩阵 。

矩阵的行表示决策(我们要选择行),一共有9*9*9个决策:在r行c列的格子里填上数字v;

矩阵的列表示任务(目的是使得每列有且仅有1个1),一共有4种任务:

1.a行b列有数字

2.a行有数字b

3.a列有数字b

4.第a个方块要有数字b

所以一共有9*9*4列;

这样9*9的数独问题就转化成9*9*9行,9*9*4列的精确覆盖问题

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<ctime>
#include<cctype>
#include<cmath>
#include<string>
#include<cstring>
#include<stack>
#include<queue>
#include<list>
#include<vector>
#include<map>
#include<set>
#define sqr(x) ((x)*(x))
#define LL long long
#define itn int
#define INF 0x3f3f3f3f
#define PI 3.1415926535897932384626
#define eps 1e-10
#define mm

using namespace std;

const int maxc=9*9*4+10;
const int maxnode=9*9*9*4+10;
const int maxr=9*9*9+10;

struct DLX
{
    int n,sz;//列数,结点数
    int S[maxc];//各列结点数
    int row[maxnode],col[maxnode];//各点行列编号
    int L[maxnode],R[maxnode],U[maxnode],D[maxnode];//十字链表
    int ansd,ans[maxr];//解

    void init(int 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
=0;L[0]=n;
        sz=n+1;
        memset(S,0,sizeof S);
    }

    //插入决策行  _r 行号  columns 记录结点列号·[1,n]
    void add_row(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;
    }

    //删除一列结点
    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 _resume(int _c)
    {
        for (int i=U[_c];i!=_c;i=U[i])
            for (int j=L[i];j!=i;j=L[j])
            {
                U[D[j]]=j;D[U[j]]=j;S[col[j]]++;
            }

        L[R[_c]]=_c;
        R[L[_c]]=_c;
    }

    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);
        for (int i=D[_c];i!=_c;i=D[i])
        {
            ans[d]=row[i];
            for (int j=R[i];j!=i;j=R[j])
                _remove(col[j]);

            if (dfs(d+1))   return true;

            for (int j=L[i];j!=i;j=L[j])//反向恢复
                _resume(col[j]);
        }
        _resume(_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;
    }

};

int encode(int a,int b,int c)
{
    return a*81+b*9+c+1;
}

void decode(int code,int &a,int &b,int &c)
{
    code--;

    c=code%9; code/=9;
    b=code%9; code/=9;
    a=code;
}

char sudoku[100];
DLX solver;

int main()
{
    #ifndef ONLINE_JUDGE
        freopen("/home/fcbruce/文档/code/t","r",stdin);
    #endif // ONLINE_JUDGE

    while (scanf("%s",sudoku),strcmp(sudoku,"end"))
    {

        solver.init(9*9*4);
        for (int i=0;i<9;i++)
        {
            for (int j=0;j<9;j++)
            {
                for (int v=0;v<9;v++)
                {
                    if (sudoku[i*9+j]=='.' || sudoku[i*9+j]=='1'+v)
                    {
                        vector<int> columns;
                        columns.push_back(encode(0,i,j));//i行j列要有数字
                        columns.push_back(encode(1,i,v));//i行要有数字v
                        columns.push_back(encode(2,j,v));//j列要有数字v
                        columns.push_back(encode(3,(i/3)*3+j/3,v));//第(i/3)*3+j/3个方块要有数字v
                        solver.add_row(encode(i,j,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);
            sudoku[r*9+c]='1'+v;
        }

        puts(sudoku);
    }

    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: