POJ 3074 Sudoku(数据结构,DLX)

18 篇文章 0 订阅
1 篇文章 0 订阅

http://poj.org/problem?id=3074


Sudoku
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 8141 Accepted: 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



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


DLX模板:

http://blog.csdn.net/u012965890/article/details/35258589


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[n]=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;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值