POJ 3074 Sudoku 转化精确覆盖问题DLX

来源:互联网 发布:pnp网络摄像机软件 编辑:程序博客网 时间:2024/05/22 04:25

题目:

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

题意:

每次给出一行81个字符,代表一个99的数独矩阵,其中"."代表没有填数字,要求把这个数独矩阵填满,并输出出来,输出同样是一行

思路:

大概我学跳舞链的初衷就是为了解数独问题?之前用dfs写过超时了,然后百度了一个思路很难想的dfs才给过,效率还是不怎么样,然后今天用跳舞链,真是快的飞起。

可以发现,数独问题有以下四个约束:

  1. 对于每一行,每个数字只能出现一次
  2. 对于每一列,每个数字只能出现一次
  3. 对于每一块,每个数字只能出现一次
  4. 对于每一个位置,都要有数字填入

将其转化为一个精确覆盖问题,用行表示状态,用列表示约束。对于数独矩阵中的每一个位置,如果当前填入了数字,肯定只有一种状态,若还没有填入数字,那么有9种状态,最大的状态数量为999=729;对于每个约束,可以用81列去表示,就是814=324。按照上述所说依次插入到相应位置,转化为精确覆盖问题,然后套用跳舞链模板即可

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <cmath>using namespace std;const int X = 100000 + 10, N = 800 + 10, M = 400 + 10, INF = 0x3f3f3f3f;struct DLX{    int U[X], D[X], L[X], R[X], row[X], col[X];    int H[N], S[M];    int head, sz, tot, n, m, ans[N];    void init(int _n, int _m)    {        n = _n, m = _m;        for(int i = 0; i <= m; i++)            L[i] = i-1, R[i] = i+1, U[i] = D[i] = i, S[i] = 0;        head = 0, tot = 0, sz = m;        L[head] = m, R[m] = head;        for(int i = 1; i <= n; i++) H[i] = -1;    }    void link(int r, int c)    {        ++S[col[++sz]=c];        row[sz] = r;        D[sz] = D[c], U[D[c]] = sz;        U[sz] = c, D[c] = sz;        if(H[r] < 0) H[r] = L[sz] = R[sz] = sz;        else R[sz] = R[H[r]], L[R[H[r]]] = sz, L[sz] = H[r], R[H[r]] = sz;    }    void del(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])                D[U[j]] = D[j], U[D[j]] = U[j], --S[col[j]];    }    void recover(int c)    {        for(int i = U[c]; i != c; i = U[i])            for(int j = L[i]; j != i; j = L[j])                D[U[j]] = U[D[j]] = j, ++S[col[j]];        R[L[c]] = L[R[c]] = c;    }    bool dance(int dep)    {        if(R[head] == head)        {            tot = dep-1; return true;        }        int c = R[head];        for(int i = R[head]; i != head; i = R[i])            if(S[i] < S[c]) c = i;        del(c);        for(int i = D[c]; i != c; i = D[i])        {            ans[dep] = row[i];            for(int j = R[i]; j != i; j = R[j]) del(col[j]);            if(dance(dep + 1)) return true;            for(int j = L[i]; j != i; j = L[j]) recover(col[j]);        }        recover(c);        return false;    }}dlx;void calc(int x, int y, int k, int len){    int r = ((x-1) * len + (y-1)) * len + k;    dlx.link(r, (x-1) * len + k);    dlx.link(r, len*len + (y-1) * len + k);    int base = sqrt(1.0 * len);    int block = (x-1) / base * base + (y-1) / base + 1;//计算出所在块    dlx.link(r, len*len*2 + (block-1) * len + k);    dlx.link(r, len*len*3 + (x-1) * len + y);}void print(int len){    sort(dlx.ans + 1, dlx.ans + 1 + dlx.tot);    for(int i = 1; i <= len; i++)        for(int j = 1; j <= len; j++)            printf("%d", dlx.ans[(i-1)*len+j] - ((i-1)*len + (j-1)) * len);    printf("\n");}int main(){    char str[N];    int len = 9;    while(scanf("%s", str+1), str[1] != 'e')    {        dlx.init(len * len * len, len * len * 4);        for(int i = 1; i <= len; i++)            for(int j = 1; j <= len; j++)                if(str[(i-1)*len+j] == '.')                    for(int k = 1; k <= len; k++) calc(i, j, k, len);                else calc(i, j, str[(i-1)*len+j]-'0', len);        dlx.dance(1);        print(len);    }    return 0;}
原创粉丝点击