数独求解 DFS DLX
题目:Sudoku
题意:求解数独。从样例和结果来看应该是简单难度的数独
思路:DFS
设置3个数组,row[i][j] 判断第i行是否放了j数字,col[i][j] 判断第i列是否放了j数字。square[i/3][j/3][x]判断第i/3行第j/3列个宫是否放置了x数字;
#include <iostream> #include <algorithm> #include <stdlib.h> #include <time.h> #include <cmath> #include <cstdio> #include <string> #include <cstring> #include <vector> #include <queue> #include <stack> #include <set>#define c_false ios_base::sync_with_stdio(false); cin.tie(0) #define INF 0x3f3f3f3f #define INFL 0x3f3f3f3f3f3f3f3f #define zero_(x,y) memset(x , y , sizeof(x)) #define zero(x) memset(x , 0 , sizeof(x)) #define MAX(x) memset(x , 0x3f ,sizeof(x)) #define swa(x,y) {LL s;s=x;x=y;y=s;} using namespace std ; #define N 50005 const double PI = acos(-1.0); typedef long long LL ;bool col[10][10], row[10][10], square[5][5][10]; char mapp[10]; int MAP[10][10]; int n;bool dfs(int z){if(z>=81) return true;int x = z/9;int y = z%9;if(MAP[x][y])return dfs(z+1);for(int i = 1; i<= 9; i++){if(!row[x][i] && !col[y][i] && !square[x/3][y/3][i]){MAP[x][y] = i;row[x][i] = col[y][i] = square[x/3][y/3][i] = 1;if(dfs(z+1))return true;MAP[x][y] = 0;row[x][i] = col[y][i] = square[x/3][y/3][i] = 0;}}return false; } int main(){//freopen("in.txt","r",stdin);//freopen("out.txt","w",stdout);scanf("%d", &n);while(n--){//memset(MAP, 0, sizeof(MAP));memset(row, false, sizeof(row));memset(col, false, sizeof(col));memset(square, false, sizeof(square));for(int i = 0; i<9; i++){scanf("%s", mapp);for(int j = 0; j<9; j++){MAP[i][j] = mapp[j] - '0';int c = MAP[i][j];if(MAP[i][j]) {row[i][c] = col[j][c] = square[i/3][j/3][c] = 1;}}}dfs(0);for(int i=0;i<9;i++){for(int j=0;j<9;j++)printf("%d",MAP[i][j]);printf("\n");}}return 0; }
View Code
题目:poj 3074
思路:DLX是从数据结构角度优化01矩阵的精确覆盖和重复覆盖的一种数据结构;
它用十字链表只存储矩阵中的非0元,而01矩阵精确覆盖dfs过程中矩阵会越来越稀疏而且每次恢复现场会浪费大量时间,DLX同时解决了这两个问题。
本题关键在于将数独问题转化为01矩阵精确覆盖。
精确覆盖定义:
满足以下条件的集合为一个精确覆盖:
- S*中任意两个集合没有交集,即X中的元素在S*中出现最多一次
- S*中集合的全集为X,即X中的元素在S*中出现最少一次
合二为一,即X中的元素在S*中出现恰好一次。
举例:
令 = {N, O, E, P} 是集合X = {1, 2, 3, 4}的一个子集的集合,并满足:
- N = { }
- O = {1, 3}
- E = {2, 4}
- P = {2, 3}.
其中一个子集 {O, E} 是 X的一个精确覆盖,因为 O = {1, 3} 而 E = {2, 4} 的并集恰好是 X = {1, 2, 3, 4}。同理, {N, O, E} 也是 X.的一个精确覆盖。空集并不影响结论。
矩阵表示法:
包含关系可以用一个关系矩阵表示。. 矩阵每行表示S的一个子集,每列表示X中的一个元素。矩阵行列交点元素为1表示对应的元素在对应的集合中,不在则为0.
通过这种矩阵表示法,求一个精确覆盖转化为求矩阵的若干个行的集合,使每列有且仅有一个1。同时,该问题也是精确覆盖的典型例题之一。
下图为其中一个例子:
1 | 2 | 3 | 4 | 5 | 6 | 7 | |
A | 1 | 0 | 0 | 1 | 0 | 0 | 1 |
B | 1 | 0 | 0 | 1 | 0 | 0 | 0 |
C | 0 | 0 | 0 | 1 | 1 | 0 | 1 |
D | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
E | 0 | 1 | 1 | 0 | 0 | 1 | 1 |
F | 0 | 1 | 0 | 0 | 0 | 0 | 1 |
我们可以发现B,D,F恰好保证每一列有且仅有一个1;
S* = {B, D, F} 便是一个精确覆盖。
DLX算法简述:
如果读取到一个格子是空的,那么加9行,分别表示这个格子填1到9这9个数字,如果读取到的格子是一个数字,那么就加一行就可以了,然后列有9*9*4列,前81列表示这一行表示填的是第i行第j列的格子,接下来81列表示第i行填写k,接下来81列表示第j列填写k,最后81列表示对应九宫格填写k。
步骤:
然后,就是正式的深搜了。在深搜的每一层:
1) 在十字链表表头中找出元素最少且非零的一列,将该列以及列中包含的所有行元素从十字链表中移除;
2) 枚举列中的每行(作为解的一部分),将与该行相交的所有其他列以及这些列包含的行元素在十字链表中移除,递归下一层深搜;
3) 递归返回时,需要将2)中移除的相应列和行添加回十字链表;
4) 所有行枚举完毕时,需要将1)中移除的相应列和行添加回十字链表。
注意,对链表进行增删操作时,需要以相反的顺序修改各结点。同时,也需要动态维护表头的元素数。
#include<iostream> #include<cstring> #include<string> #include<cstdio> #include<algorithm> #include<vector> #include<algorithm>using namespace std; // 列:(行+列+块)*9种可能+9*9个格子 // 行: 9*9*9 表示第i行第j列填k const int MAXN=(9+9+9)*9+9*9+9*9*9*9*9*4+10; #define INF 0xFFFFFF int size; int head,sz; int U[MAXN],D[MAXN],L[MAXN],R[MAXN]; int H[MAXN],ROW[MAXN],C[MAXN],S[MAXN],O[MAXN];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]){U[D[j]]=U[j];D[U[j]]=D[j];--S[C[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; }bool dfs(int k) {if(R[head]==head){sort(O,O+9*9);int p=0;for(int i=0;i<9;i++){for(int j=0;j<9;j++){int num=O[p++];//cout<<num<<endl;num=num-(i*9+j)*9;printf("%d",num);}}printf("\n");return true;}int s=INF,c;for (int t=R[head];t!=head;t=R[t]){if (S[t]<s){s=S[t];c=t;}}remove(c);for(int i=D[c];i!=c;i=D[i]){O[k]=ROW[i];for(int j=R[i];j!=i;j=R[j])remove(C[j]);if(dfs(k+1))return true;for(int j=L[i];j!=i;j=L[j])resume(C[j]);}resume(c);return false; }void initDL(int n) {head=0;for(int i=0;i<=n;i++){U[i]=i;D[i]=i;L[i]=i-1;R[i]=i+1;S[i]=0;}R[n]=0;L[0]=n;S[0]=INF+1;sz=n+1;memset(H,0,sizeof(H)); }void insert(int i, int j) {if(H[i]){L[sz]=L[H[i]];R[sz]=H[i];L[R[sz]]=sz;R[L[sz]]=sz;}else{L[sz]=sz;R[sz]=sz;H[i]=sz;}U[sz]=U[j];D[sz]=j;U[D[sz]]=sz;D[U[sz]]=sz;C[sz]=j;ROW[sz]=i;++S[j];++sz; }char str[200];void build() {int p=0;initDL(9*9*4);for(int i=0;i<9;i++)for(int j=1;j<=9;j++,p++){int base=(i*9+j-1)*9;if(str[p]=='.'){for(int k=1;k<=9;k++){int r;r=base+k;//第i行有数字kinsert(r,i*9+k);//第j列有数字kinsert(r,9*9+(j-1)*9+k);//第k块有数字kint block=(j-1)/3*3+i/3;insert(r,9*9*2+block*9+k);//第i行j列有一个数字(限制一个格子只填一个数)insert(r,9*9*3+i*9+j);}}else{int k=str[p]-'0';int r=base+k;//第i行有数字kinsert(r,i*9+k);//第j列有数字kinsert(r,9*9+(j-1)*9+k);//第k块有数字kint block=(j-1)/3*3+i/3;insert(r,9*9*2+block*9+k);//第i行j列有一个数字(限制一个格子只填一个数)insert(r,9*9*3+i*9+j);}} }int main() {size=9; //9*9数独while(~scanf("%s",str)){if(strcmp(str,"end")==0)break;build();dfs(0);}return 0; }
View Code
转载于:https://www.cnblogs.com/yoyo-sincerely/p/5309522.html
数独求解 DFS DLX相关推荐
- 【位运算DFS/DLX】【HDU1426】【数独】
题意:标准的一道数独题 DFS做法: 将横纵九宫格里的数字用位运算状态压缩,且可以通过逻辑或来确定总共有哪些数字被选择了,很方便也很快,代码如下 #include <cstdio> #in ...
- C++数独求解器与生成器
前几天笔者外出培训,刚刚学习了深度优先搜索,突然想到了数独的求解其实也可以用深搜实现,遂写了数独求解器与生成器. 1 数独求解器 1.1 预备 一开始,当然是头文件~ #include <ios ...
- 武士数独求解思路+代码实现(部分)
武士数独求解问题 老师布置了一个作业,用代码求解武士数独问题,我直接用dfs+一些优化做出来了(偷了一个大懒,具体大家看代码吧),图片就是武士数独,时间关系就不排版了.核心代码就是dfs函数,我用an ...
- 使用C#编程解决数独求解过程(从图片识别到数独求解)第二篇
这是在C#开发平台上借助Emgucv和Tessnet实现的,通过图像识别的方式求解手机app中"数独"应用中的九宫格. 预处理:emgucv-windesktop 3.1.0.22 ...
- linux下多线程验证数独,6.6.1 数独求解服务器
6.6.1 数独求解服务器 假设有这么一个网络编程任务:写一个求解数独的程序(Sudoku Solver),并把它做成一个网络服务. Sudoku Solver 是我喜爱的网络编程例子,它曾经出现在& ...
- MATLAB 自动数独求解器(导入图片自动求解)
做了一个导入图片自动求解数独的软件,不过由于目前是通过最小二乘法匹配数字的,所以导入图片中的数字最好不要是手写的..,图片大概就像这样: 使用效果: 完整代码: function sudokuApp ...
- 数独输出Java_java – 使用回溯的数独求解器
我最近一直致力于回溯数独求解算法,目前我想询问我应该如何将我的solve()方法从void更改为boolean. 我正在使用一个非常简单的回溯算法,它目前工作正常,但我宁愿有一个布尔值而不是一个空格, ...
- 编程之美之数独求解器的C++实现方法
编程之美的第一章的第15节,讲的是构造数独,一开始拿到这个问题的确没有思路, 不过看了书中的介绍之后, 发现原来这个的求解思路和N皇后问题是一致的, 但是不知道为啥,反正一开始确实没有想到这个回溯法, ...
- php 数独求解,php求解数独
<?php /* php数独求解,时间大约在1分钟 */ $nums[0] = array(0, 5, 0, 0, 1, 0, 0, 0, 9, ); $nums[1] = array( ...
最新文章
- iOS-禁止scrollview垂直方向滚动,只允许水平方向滚动;或只允许垂直方向滚动...
- 全球及中国天然香豆素行业竞争态势与投资份额调研报告2022版
- django-TDD
- Qt在VS2010的安装与配置
- java swing jbutton_Java 反射
- mockJs文档(一)
- 详解vector容器(应用+模拟实现,vector相关练习题)
- LeetCode 1554. 只有一个不同字符的字符串(枚举)
- fis3+vue+pdf.js制作预览PDF文件或其他
- 数据结构-一元多项式加减程序
- 初一音乐计算机教学方案,人音版七年级音乐教案
- 证明一下拉普拉斯的《概率分析论》观点
- 具有左,中或右对齐项的Bootstrap NavBar
- 计算机网络运输层两种服务,计算机网络体系结构及协议之运输层
- 软件项目管理:软件工具与开发环境相关知识介绍
- 华为手机屏幕锁屏时间设置_华为手机锁屏时间怎么设置?
- (转)C#中 DirectoryEntry组件应用实例
- 微信小程序实现将图片保存到手机相册
- 什么是运行时应用程序自我保护(RASP)Runtime Application Self-Protection
- 导出表钩子之EAT HOOK解析