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 }
View Code

 

posted on 2019-08-12 09:25  itdef  阅读(645)  评论(0编辑  收藏  举报

导航