基于舞蹈链的数独游戏设计(中国地质大学软件工程系实习)

本文介绍了舞蹈链(DancingLinks,DLX)算法在精确覆盖问题中的应用,通过实例演示了booldance函数的工作原理,包括节点结构、初始化和插入操作。深入解析了递归过程及如何通过DLX解决实际问题。
摘要由CSDN通过智能技术生成

参考跳跃的舞者,舞蹈链(Dancing Links)算法——求解精确覆盖问题 - 万仓一黍 - 博客园

浅谈DLX - 钱逸凡 的博客 - 洛谷博客

2022.5.3补充在网上收集到的一种实现方法,加了自己的部分理解注释,供参考

作者:brealid
链接:https://www.zhihu.com/question/461215932/answer/1903845964
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

bool dance() {
        if (!node[0].r) return true;


        int minimum_col = node[0].r;//找到该列中1最少的列
        for (int i = node[0].r; i != 0; i = node[i].r)
            if (siz[i] < siz[minimum_col]) minimum_col = i;
        remove(minimum_col);//选择该列

        result.push_back(0);//和后面的back()是一个整体操作,先开辟出一个位置,然后存(由于不知道具体存哪一行,所以在后面会不断更新,所以两个操作分开)
        for (int i = node[minimum_col].d; i != minimum_col; i = node[i].d) {
            result.back() = node[i].row;//在该列中一行一行尝试
            for (int j = node[i].l; j != i; j = node[j].l) remove(node[j].col);


            if (dance()) return true;//如果本行可以得到解,返回true------------在这里递归了,最后只需要不成功返回false即可,不用再下一列

            for (int j = node[i].r; j != i; j = node[j].r) recover(node[j].col);//如果不能,恢复本行,依序下一次循环选该列的下一行
        }
        result.pop_back();//是对最后一次选择行还不成功进行删除操作

        recover(minimum_col);//列不成功,返回false
        return false;
    }

//行数,列数,最多有几个1(DLX中的1,有很多种意思的)
void init(int row, int col, int count_of_1) {
        cnt = col;
        first.assign(row + 1, 0);//两种用法,一是将区间[first,last)
        的元素复制到当前vector容器中,二是赋n个值为x的元素到vector容器中,这个容器会清除掉vector容器中以前的内容,这里是后者

        siz.assign(col + 1, 0);//row+1和col+1是因为有列头节点和行头节点
        node.assign(col + count_of_1 + 1, DancingLinksX_Node());//注意是最多有几个1,这里的创建节点只是空节点并且可能还大于后续实际用到的节点数量,注意这里在后面的插入操作的理解

        for (int i = 0; i <= col; ++i) {
            node[i].l = i - 1;
            node[i].r = i + 1;
            node[i].u = node[i].d = i;
        }
        node[0].l = col;//第一个节点的左指针和最后一给节点的右指针的方向 为了形成循环
        node[col].r = 0;
    }


void insert(int r, int c) {
        DancingLinksX_Node &self = node[++cnt];
        self.row = r;
        self.col = c;
        ++siz[c];
        self.u = node[c].u;
        node[self.u].d = cnt;
        self.d = c;
        node[c].u = cnt;
        if (!first[r]) first[r] = self.l = self.r = cnt;///如果是插入第一个节点     (没有左指针,并且该第一个节点自我循环了)行头节点是不参与循环的,只是为了方便指示该行的第一个节点
        else {
            self.l = node[first[r]].l;//如果是加入到末尾,则当前节点的左指针指向之前的最右节点,那么借助first[r]方便找到
            self.r = first[r];
            node[node[first[r]].l].r = cnt;
            node[first[r]].l = cnt;
        }
        //for (int j = node[first[r]].l; j != first[r]; j = node[j].l);
        //for (int j = node[first[r]].r; j != first[r]; j = node[j].r);
        //for (int j = node[cnt].l; j != cnt; j = node[j].l);
        //for (int j = node[cnt].r; j != cnt; j = node[j].r);
    }

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值