Sudoku DLX算法求解

10 篇文章 0 订阅
3 篇文章 0 订阅

---------------------------------------- .h
#ifndef _DLX_SUDOKU_H_
#define _DLX_SUDOKU_H_
#include <string>

using namespace std;


#define RR      729             // 81 * 9
#define CC      324             // 81 * 4

struct node
{
    int r,c;
    node *up;
    node *down;
    node *left;
    node *right;
};

// dlx 算法求数独的解
class CDlxSudoku
{
public:
    CDlxSudoku(bool bChkUnique = true);
    int solve(string & strSudoku);
    void printfSolve();
   
private:
    inline void link(int r,int c)
    {
        m_cnt[c]++;
        node *t=&m_all[m_all_t++];
        t->r=r;
        t->c=c;
        t->left=m_row[r].left;
        t->right=&m_row[r];
        t->left->right=t->right->left=t;
       
        t->up=m_col[c].up;
        t->down=&m_col[c];
        t->up->down=t->down->up=t;
    }
   
    inline void remove(int c)
    {
        node *t,*tt;
        m_col[c].right->left=m_col[c].left;
        m_col[c].left->right=m_col[c].right;
        for(t=m_col[c].down;t!=&m_col[c];t=t->down)
        {
            for(tt=t->right;tt!=t;tt=tt->right)
            {
                m_cnt[tt->c]--;
                tt->up->down=tt->down;
                tt->down->up=tt->up;
            }
        }
    }
   
    inline void resume(int c)
    {
        node *t,*tt;
        for(t=m_col[c].down;t!=&m_col[c];t=t->down)
        {
            for(tt=t->left;tt!=t;tt=tt->left)
            {
                m_cnt[tt->c]++;
                tt->down->up=tt;
                tt->up->down=tt;
            }
        }   
        m_col[c].left->right=&m_col[c];
        m_col[c].right->left=&m_col[c];
    }

    int serch(int k);
   

    bool m_bChkUnique;  // 做解是否唯一的校验
    char m_ch[81];      // 输入字符,已经转换为(0-9)
    node m_head;
    node m_all[RR*4];
    node m_row[RR];
    node m_col[CC];
    int  m_cnt[CC];
    int  m_mem[81];     // 保存选择的行号
    int  m_rslt[81];    // 保存解中选择的行号
    int  m_ans[81];     // 答案 9*9
    int  m_all_t;       // m_all[] 中使用元素的下标
};


#endif  // _DLX_SUDOKU_H_

 

// -----------------------------------------.cpp

CDlxSudoku::CDlxSudoku(bool bChkUnique /*= true*/)
{
    m_bChkUnique = bChkUnique;
}

int CDlxSudoku::serch(int k)
{
    if(m_head.right==&m_head)
    {
        memcpy(m_rslt, m_mem, sizeof(m_rslt));
        return 1;
    }

    node*t,*tt;
    int min=RR+1;   // 每列的元素个数不会超过 RR
    int tc;
    for(t=m_head.right;t!=&m_head;t=t->right)
    {
        if(m_cnt[t->c]<min)
        {
            min=m_cnt[t->c];
            tc=t->c;
            if(min<=1)break;
        }
    }
    remove(tc);
    int scnt = 0;
    for(t=m_col[tc].down;t!=&m_col[tc];t=t->down)
    {
        m_mem[k]=t->r;
        for(tt=t->right;tt!=t;tt=tt->right)
        {
            remove(tt->c);
        }

        scnt += serch(k+1);
       
        // 不检测唯一性且解为1 或者 检测唯一性时解大于1
        if (!m_bChkUnique && scnt==1 || scnt>1)
            return scnt;
       
        for(tt=t->left;tt!=t;tt=tt->left)
        {
            resume(tt->c);
        }
    }
    resume(tc);
    return scnt;
}

int CDlxSudoku::solve(string & strSudoku)
{
    int i = 0;
    int j = 0;
    while (i < strSudoku.length())
    {
        char num = (strSudoku.at(i)=='.') ? 0 : (strSudoku.at(i)-'0');
        if (num >=0 && num <=9)
        {
            m_ch[j++] = num;
            if (j >= 81) break;
        }
       
        ++i;
    }

    m_all_t=0;
    memset(m_cnt,0,sizeof(m_cnt));
    m_head.left=&m_head;
    m_head.right=&m_head;
    m_head.up=&m_head;
    m_head.down=&m_head;
    m_head.r=RR;
    m_head.c=CC;
    for(i=0;i<CC;i++)
    {
        m_col[i].c=i;
        m_col[i].r=RR;
        m_col[i].up=&m_col[i];
        m_col[i].down=&m_col[i];
        // 将col[i]插入在 head.left 和 head之间 (横行链表, 行的最右边)
        m_col[i].left=m_head.left;
        m_col[i].right=&m_head;
        m_col[i].left->right=&m_col[i];
        m_col[i].right->left=&m_col[i];
    }
    for(i=0;i<RR;i++)
    {
        m_row[i].r=i;
        m_row[i].c=CC;
        m_row[i].left=&m_row[i];
        m_row[i].right=&m_row[i];
        // 将row[i] 插入在 head.up 和 head 之间 (纵向链表, 列的最下边)
        m_row[i].up=m_head.up;
        m_row[i].down=&m_head;
        m_row[i].up->down=&m_row[i];
        m_row[i].down->up=&m_row[i];
    }
    for(i=0;i<RR;i++)
    {
        int r=i/9/9%9;
        int c=i/9%9;
        int val=i%9+1;
        char q = m_ch[r*9+c];
        if(q==0||q==val)
        {
            link(i,r*9+val-1);
            link(i,81+c*9+val-1);
            int tr=r/3;
            int tc=c/3;
            link(i,162+(tr*3+tc)*9+val-1);
            link(i,243+r*9+c);
        }
    }
    // 将 m_row[x] 从横向链表中删除
    for(i=0;i<RR;i++)
    {
        m_row[i].left->right=m_row[i].right;
        m_row[i].right->left=m_row[i].left;
    }
   
    int num=serch(0);
    // printf("num=%d/n",num);
    int k;
    for(i=0;i<81;i++)
    {
        j=m_rslt[i]/9%81;
        k=m_rslt[i]%9+1;
        m_ans[j]=k;
    }
   
//    for(i=0;i<81;i++)
//        printf("%d",m_ans[i]);
//    printf("/n");

//    printfSolve();
   
    return num;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值