POJ3074_Sudoku_部分固定的跳舞链

题意

数独游戏。

思路

部分固定的跳舞链

链接

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

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int n = 9;
const int maxn = n * n * n + 10;
const int maxm = n * n * 3 + n * n + 10;
const int maxv = maxn * maxm;

struct DLX
{
    int n, m, size;                                                 //行数、列数、节点数
    int U[maxv], D[maxv], L[maxv], R[maxv], Row[maxv], Col[maxv];   //上、下、左、右指针,行、列号
    int S[maxm], H[maxn];                                           //列的节点个数,行的头指针
    int ansd, ans[maxn];                                            //答案栈

    void init(int _n, int _m)                                       //初始化函数
    {
        n = _n, m = _m;

        for(int j= 0; j<= m; j++)
        {
            Row[j] = 0, Col[j] = j;                                 //行、列号

            U[j] = D[j] = j;                                        //上下指针
            S[j] = 0;

            L[j] = j - 1, R[j] = j + 1;                             //左右指针
        }
        L[0] = m, R[m] = 0;                                         //端点左右指针

        size = m;                                                   //节点个数(初始 = 列数)(附加节点)

        for(int i= 1; i<= n; i++)                                   //行的头指针
            H[i] = -1;
    }

    void link(int r, int c)                                         //向链表中插入节点
    {
        size ++;                                                    //节点数目
        Row[size] = r, Col[size] = c;                               //行号、列号

        S[c] ++;                                                    //插入列链表
        D[size] = D[c];
        U[D[c]] = size;
        D[c] = size;
        U[size] = c;

        if(H[r] == -1) H[r] = L[size] = R[size] = size;             //将节点插入行链表
        else{                                                       //第一个节点之后
            R[size] = R[H[r]];
            L[R[H[r]]] = size;
            L[size] = H[r];
            R[H[r]] = size;
        }
    }

    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)                                              //恢复列函数(同时恢复列中节点所在的行
    {
        R[L[c]] = L[R[c]] = 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]] ++;
            }
    }

    bool Dance(int d)                                               //搜索函数
    {
        if(R[0] == 0)                                               //剩余列为空,搜索成功
        {
            ansd = d;
            return true;
        }

        int c = R[0];                                               //从节点少的列开始搜索,可以减少状态数
        for(int j= R[0]; j!= 0; j= R[j])
            if(S[j] < S[c]) c = j;

        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(Dance(d+1)) return true;                             //向下一层搜索
            for(int j= L[i]; j!= i; j= L[j]) resume(Col[j]);        //搜索失败的话还原当前行
        }

        resume(c);
        return false;
    }
};

int G[n * n + 1][n * n + 1];
char in[n * n];
DLX g;
bool a[n + 1][n + 1], b[n + 1][n + 1], c[n + 1][n + 1];

int main(){
    while(1){
        scanf(" %s", in);
        if(in[0] == 'e') break;

        for(int i = 0; i < n * n; i++){
            int x = i / 9 + 1, y = i % 9 + 1;
                if(in[i] == '.') G[x][y] = 0;
                else G[x][y] = in[i] - '0';
        }

        memset(a, false, sizeof a);
        memset(b, false, sizeof b);
        memset(c, false, sizeof c);

        g.init(n * n * n, n * n * 3 + n * n);

        //先把固定值建图
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j++){
                if(G[i][j] > 0){
                    a[i][G[i][j]] = b[j][G[i][j]] = c[(i - 1) / 3 * 3 + (j - 1) / 3 + 1][G[i][j]] = true;
                    g.link((i - 1) * n * n + (j - 1) * n + G[i][j], (i - 1) * n + G[i][j]);
                    g.link((i - 1) * n * n + (j - 1) * n + G[i][j], n * n + (j - 1) * n + G[i][j]);
                    g.link((i - 1) * n * n + (j - 1) * n + G[i][j], n * n * 2 + ((i - 1) / 3 * 3 + (j - 1) / 3 + 1 -1) * n + G[i][j]);
                    g.link((i - 1) * n * n + (j - 1) * n + G[i][j], n * n * 3 + (i - 1) * n + j);
                }
            }
        }

        //空白格建图
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j++){
                if(G[i][j] > 0) continue;//避开与固定值的冲突

                for(int k = 1; k <= n; k++){
                    if(a[i][k] || b[j][k] || c[(i - 1) / 3 * 3 + (j - 1) / 3 + 1][k]) continue;
                    g.link((i - 1) * n * n + (j - 1) * n + k, (i - 1) * n + k);
                    g.link((i - 1) * n * n + (j - 1) * n + k, n * n + (j - 1) * n + k);
                    g.link((i - 1) * n * n + (j - 1) * n + k, n * n * 2 + ((i - 1) / 3 * 3 + (j - 1) / 3 + 1 -1) * n + k);
                    g.link((i - 1) * n * n + (j - 1) * n + k, n * n * 3 + (i - 1) * n + j);
                }
            }
        }

        g.Dance(0);

        for(int i = 0; i < g.ansd; i++){
            int x = (g.ans[i] - 1) / (n * n) + 1, y = (g.ans[i] - 1) % (n * n) / n + 1, k = (g.ans[i] - 1) % n + 1;
            G[x][y] = k;
        }

        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j++){
                cout << G[i][j];
            }
        }
        cout << endl;
    }

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值