现在的位置: 首页 > 综合 > 正文

DLX精确覆盖解数独问题

2013年03月06日 ⁄ 综合 ⁄ 共 3717字 ⁄ 字号 评论关闭

关于Dancing Links的介绍最好仔细阅读一下Knuth的论文,那篇翻译的文章也不错,这位仁兄的 博客比较易懂http://blog.csdn.net/sunny606/article/details/7833551

通过阅读论文主要有如下几点体会:

1.精确覆盖的最基本模型是:在一个01矩阵中选择若干个行,使得没列都包含且仅包含一个1

2.Dangcing links的逻辑与普通dfs暴力搜索相似,但它利用四向循环链表进行了犀利的剪枝,使得当搜索进入下一层时链表变得很稀疏,且结合了一些启发函数,因此虽然复杂度理论上是指数级的,但实际速度很快,可以在一秒内处理1000*1000规模的矩阵。

3.四向循环链表将矩阵中所有1连接,当选取了某一行时,将与之矛盾的行列暂时从链表中删除进入下一层搜索,由于只是修改指针不是真正删除,因此在搜索失败后能快速恢复链表选取下一方案。

4.关于建模:没列代表一个限制条件,每行代表一种方案,若方案i满足限制j,则矩阵mat[i][j]=1,因此矩阵的构造既要满足所有限制条件,又不能漏掉可能的方案。

以解决数独问题为例:首先看限制:每一行每一列每一个小块中1--9都只能出现一次,且每个格子都要选取一个元素,因此有9*(9+9+9)+81个限制。

                限制:每个格子可以去所有与已有元素不矛盾的元素,每种方案都满足四种限制。

题目:poj3074 poj 2676 poj3076 hdu 4069 hdu3909

hdu3909比较综合,数独的阶数不确定,要判断无解、多解、给出的矩阵是否是最小矩阵(若去掉任意一个元素后都使得矩阵有多解则称为最小矩阵)

多解只需在dance过程中修改一下返回条件即可,找到第二个解后再返回,判断最小矩阵只需逐个去掉矩阵中的元素判多解即可。

import java.util.Arrays;
import java.util.Scanner;

public class Main{
    class DLX {
        int maxn = 20010;
        int L[] = new int[maxn], R[] = new int[maxn], D[] = new int[maxn],
                U[] = new int[maxn];
        int Row[] = new int[maxn], C[] = new int[maxn], S[] = new int[maxn];
        // 元素x所在行列 每列元素个数
        int m, id, rowid;
        int ans[] = new int[maxn], cnt;

        void init(int m) {
            this.m = m;
            mul = 0;
            for (int i = 0; i <= m; i++) {
                D[i] = U[i] = i;
                S[i] = 0;
                L[i] = i - 1;
                R[i] = i + 1;
            }
            L[0] = m;
            R[m] = 0;
            id = m + 1;
            cnt = rowid = 0;
            mul = 0;
        }

        void insert(int arr[], int len) {
            for (int i = 0; i < len; i++, id++) {
                int x = arr[i];
                C[id] = x;
                Row[id] = rowid;
                S[x]++;
                D[id] = x;
                U[id] = U[x];
                D[U[x]] = id;
                U[x] = id;
                if (i == 0)
                    L[id] = R[id] = id;
                else {
                    L[id] = id - 1;
                    R[id] = id - i;
                    L[id - i] = id;
                    R[id - 1] = id;
                }
            }
            rowid++;
        }

        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]) {
                    S[C[j]]--;
                    U[D[j]] = U[j];
                    D[U[j]] = D[j];
                }
        }

        void resume(int c) {
            for (int i = U[c]; i != c; i = U[i])
                for (int j = L[i]; j != i; j = L[j]) {
                    S[C[j]]++;
                    U[D[j]] = j;
                    D[U[j]] = j;
                }
            L[R[c]] = c;
            R[L[c]] = c;
        }

        int mul = 0;

        boolean dance() {
            if (R[0] == 0) {
                if (++mul > 1)
                    return true;
                for (int i = 0; i < cnt; i++)
                    res[p[ans[i]].i][p[ans[i]].j] = p[ans[i]].val;
                return false;
            }
            int c = R[0];
            for (int i = R[0]; i != 0; i = R[i])
                if (S[i] < S[c])
                    c = i;
            remove(c);
            for (int i = D[c]; i != c; i = D[i]) {
                ans[cnt++] = Row[i];
                for (int j = R[i]; j != i; j = R[j])
                    remove(C[j]);
                if (dance())
                    return true;
                for (int j = L[i]; j != i; j = L[j])
                    resume(C[j]);
                cnt--;
            }
            resume(c);
            return false;
        }

    }

    class plan {
        int i, j, val;

        plan(int i, int j, int v) {
            this.j = j;
            this.i = i;
            val = v;
        }
    }

    int map[][] = new int[20][20], res[][] = new int[20][20], m,
            hash[] = new int[255];
    char rehash[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A',
            'B', 'C', 'D', 'E', 'F', 'G' };
    boolean flag[] = new boolean[20];
    plan p[] = new plan[5010];
    DLX dlx = new DLX();
    Scanner scan = new Scanner(System.in);

    void build(int i, int j) {
        if (map[i][j] != 0) {
            Arrays.fill(flag, false);
            flag[map[i][j]] = true;
            return;
        }
        Arrays.fill(flag, true);
        for (int k = 1; k <= m * m; k++)
            flag[map[k][j]] = flag[map[i][k]] = false;
        i = (i - 1) / m * m + 1;
        j = (j - 1) / m * m + 1;
        for (int x = 0; x <m; x++)
            for (int y = 0; y <m; y++)
                flag[map[i + x][j + y]] = false;
    }

    int grid(int i, int j) {
        return (i - 1) / m * m + (j - 1) / m + 1;
    }

    void init() {
        int cnt = 0;
        for (int i = '0'; i <= '9'; i++)
            hash[i] = cnt++;
        for (int i = 'A'; i <= 'G'; i++)
            hash[i] = cnt++;
    }

    void work() {
        int M = m * m;
        dlx.init(M * M * 4);
        int arr[] = new int[4];
        int idx = 0;
        for (int i = 1; i <= M; i++)
            for (int j = 1; j <= M; j++) {
                build(i, j);
                for (int k = 1; k <= M; k++)
                    if (flag[k]) {
                        arr[0] = (i - 1) * M + k;// 行
                        arr[1] = M * M + (j - 1) * M + k;// 列
                        arr[2] = M * M * 2 + (grid(i, j) - 1) * M + k;// 块
                        arr[3] = M * M * 3 + (i - 1) * M + j;// 格子
                        dlx.insert(arr, 4);
                        p[idx++] = new plan(i, j, k);
                    }
            }
        dlx.dance();
    }

    void run() {
        init();
        while (scan.hasNext()) {
            m = scan.nextInt();
            for (int i = 1; i <= m * m; i++) {
                String s = scan.next();
                for (int j = 1; j <= m * m; j++)
                    map[i][j] = hash[s.charAt(j - 1)];
            }
            work();
            if (dlx.mul == 2)
                System.out.println("Multiple Solutions");
            if (dlx.mul == 0)
                System.out.println("No Solution");
            if (dlx.mul == 1)
                if (minimal()) {
                    work();
                    print();
                } else
                    System.out.println("Not Minimal");
        }
    }

    boolean minimal() {
        for (int i = 1; i <= m * m; i++)
            for (int j = 1; j <= m * m; j++)
                if (map[i][j] != 0) {
                    int temp = map[i][j];
                    map[i][j] = 0;
                    work();
                    map[i][j] = temp;
                    if (dlx.mul == 1){
                        
                        return false;
                    }
                }
        return true;
    }

    void print() {
        for (int i = 1; i <= m * m; i++) {
            for (int j = 1; j <= m * m; j++)
                System.out.print(rehash[res[i][j]]);
            System.out.println();
        }
    }

    public static void main(String[] args) {
        new Main().run();
    }
}

抱歉!评论已关闭.