poj3074-Sodoku

解数独。

分析

考虑如何把数独解合法的条件转化为经典的01精确覆盖:

  • 每个格子只能填一个数,1-9
  • 每一列刚好填了1-9
  • 每一行刚好填了1-9
  • 每个九宫格刚好填了1-9

也就是说,每个格子,列,行,九宫格都需要被一个数覆盖,且不能重复覆盖。

精确覆盖的一个很巧妙的,也很常用的建矩阵方法,是把条件拆开,把每一个填入也拆成对四种条件的贡献。

也就是说,我们建一个729*324的矩阵。所有的行表示在\((x,y)\)填入\(k\),前81列表示每个格子被覆盖,后面的各81列分别表示每一列,一行,九宫格被覆盖,那么这就是一个精确覆盖问题了——每次填入一个数,可以对应地在四个规则中产生贡献。

如果已经给到有数了,那么就\((x,y)\)就只能填入规定的那个\(k\),否则可以填1-9。

代码

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=10*10*10;
const int maxm=10*10*5;
const int maxl=10*10;
const int maxp=maxn*maxm;
bool a[maxn][maxm];
char s[maxl];
int ans[10][10];
struct node {
    int l,r,u,d,row,col;
};
int id(int x,int y) {
    return (x-1)*9+y;
}
int hang(int x,int k) {
    return 81+(x-1)*9+k;
}
int lie(int y,int k) {
    return 162+(y-1)*9+k;
}
int bel(int x,int y) {
    return ((x-1)/3)*3+(y-1)/3+1;
}
int gong(int x,int y,int k) {
    return 243+(bel(x,y)-1)*9+k;
}
int choose(int x,int y,int k) {
    return (id(x,y)-1)*9+k;
}
void ANS(int row) {
    int k=(row-1)%9+1;
    row-=k;
    int d=row/9+1;
    int y=(d-1)%9+1;
    int x=(d-y)/9+1;
    ans[x][y]=k;
}
struct DLX {
    node p[maxp];
    int tot,last[maxm],size[maxm];
    void clear(int n) {
        tot=n;
        memset(p,0,sizeof p),memset(last,0,sizeof last),memset(size,0,sizeof size);
        p[0]=(node){n,1,0,0,0,0};
        for (int i=1;i<=n;++i) p[i]=(node){i-1,i+1,i,i,0,i},last[i]=i;
        p[n].r=0;
    }
    void build(int row,int c[],int len) {
        if (!len) return;
        p[++tot]=(node){tot,tot,last[c[1]],p[last[c[1]]].d,row,c[1]};
        p[p[tot].u].d=p[p[tot].d].u=last[c[1]]=tot;
        ++size[p[tot].col];
        for (int i=2;i<=len;++i) {
            int x=c[i];
            p[++tot]=(node){tot-1,p[tot-1].r,last[x],p[last[x]].d,row,x};
            p[p[tot].l].r=p[p[tot].r].l=p[p[tot].d].u=p[p[tot].u].d=last[x]=tot;
            ++size[p[tot].col];
        }
    }
    void del(int c) {
        p[p[c].l].r=p[c].r,p[p[c].r].l=p[c].l;
        for (int i=p[c].d;i!=c;i=p[i].d) 
            for (int j=p[i].r;j!=i;j=p[j].r) 
                p[p[j].u].d=p[j].d,p[p[j].d].u=p[j].u,--size[p[j].col];
    }
    void back(int c) {
        p[p[c].l].r=p[p[c].r].l=c;
        for (int i=p[c].u;i!=c;i=p[i].u) 
            for (int j=p[i].l;j!=i;j=p[j].l) 
                p[p[j].u].d=p[p[j].d].u=j,++size[p[j].col];
    }
    bool dance() {
        if (!p[0].r) return true;
        int first=p[0].r;
        for (int i=p[0].r;i;i=p[i].r) if (size[i]<size[first]) first=i;
        if (p[first].d==first) return false;
        del(first);
        for (int i=p[first].d;i!=first;i=p[i].d) {
            for (int j=p[i].r;j!=i;j=p[j].r) del(p[j].col);
            ANS(p[i].row);
            if (dance()) return true;
            for (int j=p[i].l;j!=i;j=p[j].l) back(p[j].col);
        }
        back(first);
        return false;
    }
} dlx;
int main() {
#ifndef ONLINE_JUDGE
    freopen("test.in","r",stdin);
#endif
    while (~scanf("%s",s+1)) {
        dlx.clear(324);
        memset(a,0,sizeof a),memset(ans,0,sizeof ans);
        if (s[1]=='e') break;
        for (int i=1,k=0;i<=9;++i) for (int j=1;j<=9;++j) {
            int x=(s[++k]=='.'?0:s[k]-'0'),st=(x?x:1),ed=(x?x:9);
            ans[i][j]=x;
            for (int k=st;k<=ed;++k) {
                int cho=choose(i,j,k);
                a[cho][id(i,j)]=a[cho][hang(i,k)]=a[cho][lie(j,k)]=a[cho][gong(i,j,k)]=true;
            }
        }
        for (int i=1;i<=729;++i) {
            static int c[maxm];
            int tot=0;
            for (int j=1;j<=324;++j) if (a[i][j]) c[++tot]=j;
            dlx.build(i,c,tot);
        }
        dlx.dance();
        for (int i=1;i<=9;++i) for (int j=1;j<=9;++j) printf("%d",ans[i][j]);
        puts("");
    }
    return 0;
}

转载于:https://www.cnblogs.com/owenyu/p/6741069.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值