166. 数独 dancing links 方法
dfs硬怼通过数独 N皇后的代码后 想学习下新的数据结构和算法来解决这类覆盖问题
视频题解 https://www.bilibili.com/video/BV1WK41137iE
习题练习
https://www.acwing.com/problem/content/168/ 数独
https://www.acwing.com/problem/content/171/ 数独2
https://www.acwing.com/problem/content/185/ 靶形数独
资料收集如下 进行学习
https://www.cnblogs.com/ivan-count/p/7355431.html
https://www.cnblogs.com/grenet/p/3145800.html
https://blog.csdn.net/whereisherofrom/article/details/79220897
习题学习
洛谷
https://www.luogu.org/problem/P4929 P4929 【模板】舞蹈链(DLX)
https://www.luogu.org/problem/P1074 P1074 靶形数独
https://www.luogu.org/problem/P1219 P1219 八皇后
题解
https://www.luogu.org/blog/ONE-PIECE/qian-tan-dlx 【模板】舞蹈链(DLX)
https://www.luogu.org/blog/ONE-PIECE/ba-xing-shuo-du-dai-ma 靶形数独
https://www.luogu.org/blog/ONE-PIECE/solution-p1219 八皇后
我的对模板注释的代码 参考的源代码地址https://www.luogu.org/blog/ONE-PIECE/qian-tan-dlx

1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 5 6 //精确覆盖问题的定义:给定一个由0-1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1 7 const int MN = 9 * 9 * 9 + 10;//最大行数,共有9*9个格子,每个格子可以放1~9 8 const int MM = 9 * 9 + 9 * 9 + 9 * 9 + 9 * 9 + 100;//最大列数 9 const int MAX_NUM = MN * MM; //最大点数 10 11 struct DLX 12 { 13 int n, m, idx;//n行数m列数idx 元素索引 14 //十字链表组成部分 15 16 int l[MAX_NUM], r[MAX_NUM], u[MAX_NUM], d[MAX_NUM]; //记录某个idx索引点的上下左右点的索引 17 int col[MAX_NUM], row[MAX_NUM]; //两者结合使用 记录某个idx索引点的行 列号 18 19 20 int nodeIdxPerRow[MAX_NUM]; //记录每行的开头节点的索引 21 int nodeNumPerCol[MAX_NUM]; //记录每列节点的个数 22 23 24 int ansd, ans[MN]; 25 26 void init(int n ,int m) //初始化十字链表的 头节点和 列节点表头串 m表示有多少列 27 { 28 //初始化头结点和 列节点表头串 头节点数组索引 = 0 列节点表头共m个 索引 1~m 29 for (int i = 0; i <= m; i++) { 30 r[i] = i + 1; 31 l[i] = i - 1; 32 u[i] = d[i] = i; //列节点头串只有互相横向连接 上下连接均指向自己 33 } 34 r[m] = 0; 35 l[0] = m; //循环连接 col[m] 的右端指向头节点 头结点的左端指向clo[m] 36 37 memset(nodeIdxPerRow, 0, sizeof(nodeIdxPerRow)); 38 memset(nodeNumPerCol, 0, sizeof(nodeNumPerCol)); 39 40 idx = m + 1; //目前使用 0 头结点 与 m个列节点表头串 0~m 共m+1个节点 41 } 42 43 44 45 //插入节点 进行的一些数据记录 46 void link(int insertRow, int insertCol) 47 { 48 nodeNumPerCol[insertCol]++; //插入一个节点 那么该列的节点个数+1 49 row[idx] = insertRow; 50 col[idx] = insertCol; //记录第idx个节点所在的行与列 51 52 u[idx] = insertCol; //当前插入的节点索引记录 向上指向列节点头串中的insertCol 53 d[idx] = d[insertCol]; //当前插入节点索引记录 向下指向原来列节点头串的向下指向点 54 u[d[insertCol]] = idx; //原来列节点头串指向的节点 向上由指向列节点头串指向插入的节点(使用索引) 55 d[insertCol] = idx; //列节点头串则向下指向新插入的节点(使用索引) 56 57 //更新每行的节点记录 nodeIdxPerRow 58 if (nodeIdxPerRow[insertRow] == 0) { 59 //如果该节点是第一个插入的节点 60 nodeIdxPerRow[insertRow] = r[idx] = l[idx] = idx;//该行没有点,直接加入 61 } 62 else { 63 //如果不是第一个插入的节点 同上面处理列次序一样 在记录和第一个节点间 插入本函数插入的节点 64 r[idx] = nodeIdxPerRow[insertRow]; //新节点的右端指向原来行记录中的第一个节点 65 l[idx] = l[nodeIdxPerRow[insertRow]]; //新节点的左端指向原来行记录第一个节点的左端 也就是行记录nodeIdxPerRow 66 r[l[nodeIdxPerRow[insertRow]]] = idx; //原来行记录第一个节点的左端(也就是行记录nodeIdxPerRow)的右端 指向新插入的点(使用索引) 67 l[nodeIdxPerRow[insertRow]] = idx; //原来行记录第一个节点的左端指向新插入的节点(使用索引) 68 69 } 70 idx++; 71 return; 72 } 73 74 void remove(int deleteCol) {//删除涉及C列的集合 75 //将要删除的列的左右两端连接起来 也等于将自己摘除出来 76 r[l[deleteCol]] = r[deleteCol], l[r[deleteCol]] = l[deleteCol]; 77 for (int i = d[deleteCol]; i != deleteCol; i = d[i]) { 78 for (int j = r[i]; j != i; j = r[j]) { 79 u[d[j]] = u[j]; 80 d[u[j]] = d[j]; 81 nodeNumPerCol[col[j]]--; 82 } 83 } 84 } 85 void resume(int resCol) {//恢复涉及C列的集合 86 for (int i = u[resCol]; i != resCol; i = u[i]) { 87 for (int j = l[i]; j != i; j = l[j]) { 88 u[d[j]] = j; 89 d[u[j]] = j; 90 nodeNumPerCol[col[j]]++; 91 } 92 } 93 r[l[resCol]] = resCol; 94 l[r[resCol]] = resCol; 95 } 96 97 98 99 bool dance(int deep) //选取了d行 100 { 101 if (r[0] == 0)//全部覆盖了 102 { 103 //全覆盖了之后的操作 104 ansd = deep; 105 return 1; 106 } 107 108 109 int c = r[0];//表头结点指向的第一个列 110 for (int i = r[0]; i != 0; i = r[i])//枚举列头指针 111 { 112 if (nodeNumPerCol[i] < nodeNumPerCol[c])c = i; 113 114 } 115 116 remove(c);//将该列删去 117 for (int i = d[c]; i != c; i = d[i]) 118 {//枚举该列的元素 119 ans[deep] = row[i];//记录该列元素的行 120 for (int j = r[i]; j != i; j = r[j]) 121 remove(col[j]);//将该列的某个元素的行上的元素所在的列都删去 122 if (dance(deep + 1)) 123 return 1; 124 for (int j = l[i]; j != i; j = l[j]) 125 resume(col[j]); 126 } 127 resume(c); 128 return 0; 129 } 130 }dlx; 131 //========================================================== 132 133 134 char s[90], path[90]; 135 struct node 136 { 137 int r, c, v; 138 }nds[MN]; 139 int main() 140 { 141 while (~scanf("%s", s)) 142 { 143 if (s[0] == 'e')break; 144 dlx.init(9 * 9 * 9, 9 * 9 * 4); 145 int r = 1; 146 for (int i = 1; i <= 9; i++) 147 { 148 for (int j = 1; j <= 9; j++) 149 { 150 if (s[(i - 1) * 9 + j - 1] == '.') 151 { 152 for (int z = 1; z <= 9; z++) 153 { 154 dlx.link(r, (i - 1) * 9 + j); 155 dlx.link(r, 81 + (i - 1) * 9 + z); 156 dlx.link(r, 162 + (j - 1) * 9 + z); 157 dlx.link(r, 243 + (((i - 1) / 3) * 3 + (j + 2) / 3 - 1) * 9 + z); 158 nds[r].r = i, nds[r].c = j, nds[r].v = z; 159 r++; 160 } 161 } 162 else 163 { 164 int z = s[(i - 1) * 9 + j - 1] - '0'; 165 dlx.link(r, (i - 1) * 9 + j); 166 dlx.link(r, 81 + (i - 1) * 9 + z); 167 dlx.link(r, 162 + (j - 1) * 9 + z); 168 dlx.link(r, 243 + (((i - 1) / 3) * 3 + (j + 2) / 3 - 1) * 9 + z); 169 nds[r].r = i, nds[r].c = j, nds[r].v = z; 170 r++; 171 } 172 } 173 } 174 dlx.ansd = -1; 175 dlx.dance(0); 176 int deep = dlx.ansd; 177 for (int i = 0; i < deep; i++) 178 { 179 int posr = dlx.ans[i]; 180 path[(nds[posr].r - 1) * 9 + nds[posr].c - 1] = '0' + nds[posr].v; 181 } 182 path[deep] = '\0'; 183 printf("%s\n", path); 184 } 185 return 0; 186 }
欢迎转帖 请保持文本完整并注明出处
技术博客 http://www.cnblogs.com/itdef/
B站算法视频题解
https://space.bilibili.com/18508846
qq 151435887
gitee https://gitee.com/def/
欢迎c c++ 算法爱好者 windows驱动爱好者 服务器程序员沟通交流
如果觉得不错,欢迎点赞,你的鼓励就是我的动力


【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话