KLSudoku里使用DLX快速解数独的代码

这部分代码是从另外一个C++的程序里移植过来的,一时找不到原C++代码的连接了,但是从C#的代码反推C++的代码并不是什么问题,当时移植这段代码也没废什么功夫。

使用DLX(Dancing Link X)解决数独这个Exact Cover(精确覆盖)问题,是一个很常见的方法。类似的代码有比较多。但是也有相当的人是利用尝试和回溯的办法来解题的。

现在的计算机计算效率已经非常的高了,而数独的9X9的方阵也的确没多少数据量,所以站在人的角度,并不能感受两种解题方法在速度上的差异。

但是在生成数独题的过程中,会需要非常多的次数来重复解题,所以解题的时间自然是越快越好的。

我将KLSudoku里这个部分的代码单独在这里贴出,而完整的代码可以从项目主页SVN TAB上浏览看到:http://code.google.com/p/klsudoku

(奇怪,不能插入代码,只好直接贴了)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Collections;
using System.IO;
using System.Xml;
/*
 * Project Home : http://code.google.com/p/klsudoku
 * Project Owner: ttylikl@gmail.com Email:ttylikl@qq.com
 * Project Blog : http://www.cnblogs.com/ttylikl
 * All Files of this project is free to use , but you should keep the information above when you copy&use it.
 * Infomation Modify Date: 2009.02.22
 *
 * */
namespace SudokuPanel
{
    public class dlx_solver
    {
        const int RR = 729;
        const int CC = 324;
        const int INF = 1000000000;
        int[] mem = new int[RR + 9];
        int[] ans = new int[RR + 9];
        char[] ch = new char[RR + 9];
        int[] cnt = new int[CC + 9];
        class node
        {
            public int r, c;
            public node up;
            public node down;
            public node left;
            public node right;
        };
        node head;
        node[] all = new node[RR * CC + 99];
        node[] row = new node[RR];
        node[] col = new node[CC];
        int all_t;
        void link(int r, int c)
        {
            cnt[c]++;
            node t = all[all_t];
            if (t == null)
            {
                t = new node();
                all[all_t] = t;
            }
            all_t++;
            t.r = r;
            t.c = c;
            t.left = row[r];
            t.right = row[r].right;
            t.left.right = t;
            t.right.left = t;
            t.up = col[c];
            t.down = col[c].down;
            t.up.down = t;
            t.down.up = t;
        }
        void remove(int c)
        {
            node t, tt;
            col[c].right.left = col[c].left;
            col[c].left.right = col[c].right;
            for (t = col[c].down; !Object.ReferenceEquals(t, col[c]); t = t.down)
            {
                for (tt = t.right; !Object.ReferenceEquals(tt, t); tt = tt.right)
                {
                    cnt[tt.c]--;
                    tt.up.down = tt.down;
                    tt.down.up = tt.up;
                }
                t.left.right = t.right;
                t.right.left = t.left;
            }
        }
        void resume(int c)
        {
            node t, tt;
            for (t = col[c].down; !Object.ReferenceEquals(t, col[c]); t = t.down)
            {
                t.right.left = t;
                t.left.right = t;
                for (tt = t.left; !Object.ReferenceEquals(tt, t); tt = tt.left)
                {
                    cnt[tt.c]++;
                    tt.down.up = tt;
                    tt.up.down = tt;
                }
            }
            col[c].left.right = col[c];
            col[c].right.left = col[c];
        }
        int solve(int k)
        {
            if (Object.ReferenceEquals(head.right, head))
                return 1;
            node t, tt;
            int min = INF, tc=0;
            for (t = head.right; !Object.ReferenceEquals(t, head); t = t.right)
            {
                if (cnt[t.c] < min)
                {
                    min = cnt[t.c];
                    tc = t.c;
                    if (min <= 1) break;
                }
            }
            remove(tc);
            int scnt = 0;
            for (t = col[tc].down; !Object.ReferenceEquals(t, col[tc]); t = t.down)
            {
                //if (mem[k] != 0)
                //    Debug.Write("");
                mem[k] = t.r;
                t.left.right = t;
                for (tt = t.right; !Object.ReferenceEquals(tt, t); tt = tt.right)
                {
                    remove(tt.c);
                }
                t.left.right = t.right;
                scnt += solve(k + 1);
                if (!chk_unique && scnt ==1)
                    return scnt;
                if (scnt > 1)
                    return scnt;
                //继续找下一个可能
                t.right.left = t;
                for (tt = t.left; !Object.ReferenceEquals(tt, t); tt = tt.left)
                {
                    resume(tt.c);
                }
                t.right.left = t.left;
            }

            resume(tc);
            return scnt;
        }

        private bool chk_unique = false;
        public int scount=0;
        public int solution_count(String str)
        {
            chk_unique=true;
            run(str);
            return scount;
        }
        public String do_solve(String str)
        {
            chk_unique = false;
            String ret=run(str);
            if (scount != 1)
                return "";
            return ret;
        }
        private String run(String str)
        {
            mem = new int[RR + 9];
            ans = new int[RR + 9];
            ch = new char[RR + 9];

            //Debug.WriteLine("dlx_solve(" + str + ")");
            String s=str.Replace("\r","");
            s=s.Replace("\n","");
            ch = s.ToCharArray();

            cnt = new int[CC + 9];
            head = new node();
            all = new node[RR * CC + 99];
            row = new node[RR];
            col = new node[CC];
            /*while(gets(ch))*/
            {
                int i;
                //if(ch[0]=='e')break;
                all_t = 1;
                cnt = new int[CC + 9];
                head.left = head;
                head.right = head;
                head.up = head;
                head.down = head;
                head.r = RR;
                head.c = CC;
                for (i = 0; i < CC; i++)
                {
                    col[i] = new node();
                    col[i].c = i;
                    col[i].r = RR;
                    col[i].up = col[i];
                    col[i].down = col[i];
                    col[i].left = head;
                    col[i].right = head.right;
                    col[i].left.right = col[i];
                    col[i].right.left = col[i];
                }
                for (i = 0; i < RR; i++)
                {
                    row[i] = new node();
                    row[i].r = i;
                    row[i].c = CC;
                    row[i].left = row[i];
                    row[i].right = row[i];
                    row[i].up = head;
                    row[i].down = head.down;
                    row[i].up.down = row[i];
                    row[i].down.up = row[i];
                }
                for (i = 0; i < RR; i++)
                {
                    int r = i / 9 / 9 % 9;
                    int c = i / 9 % 9;
                    int val = i % 9 + 1;
                    if (ch[r * 9 + c] == '.' || ch[r * 9 + c] == '0' || ch[r * 9 + c] == val + '0')
                    {
                        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);
                    }
                }
                for (i = 0; i < RR; i++)
                {
                    row[i].left.right = row[i].right;
                    row[i].right.left = row[i].left;
                }
                scount = solve(1);
                for (i = 1; i <= 81; i++)
                {
                    int t = mem[i] / 9 % 81;
                    int val = mem[i] % 9 + 1;
                    //Debug.WriteLine("t=" + t.ToString("00")+ "mem[" + i.ToString("D2")+ "]="+mem[i]+" val=" + val + "i=" + i.ToString("D2"));
                    ans[t] = val;
                }
                StringBuilder sb = new StringBuilder();
                for (i = 0; i < 81; i++)
                {
                    //Debug.Write(ans[i]);
                    //if (ans[i] == 0)
                    //    Debug.Write("val=0!");
                    sb.Append(ans[i]);
                }
                //Debug.WriteLine("");
                //Debug.WriteLine("scnt=" + solution_count);
                return sb.ToString();
            }
        }
    }
}

http://code.google.com/p/klsudoku
Email: ttylikl@qq.com
QQ群:106249 ( Sudoku 群) QQ群:94388010( C++ 群)
原文地址:https://www.cnblogs.com/ttylikl/p/1438964.html