题目: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[][], row[][], square[][][];
char mapp[];
int MAP[][];
int n; bool dfs(int z){
if(z>=) return true;
int x = z/;
int y = z%;
if(MAP[x][y])
return dfs(z+);
for(int i = ; i<= ; i++){
if(!row[x][i] && !col[y][i] && !square[x/][y/][i]){
MAP[x][y] = i;
row[x][i] = col[y][i] = square[x/][y/][i] = ;
if(dfs(z+))
return true;
MAP[x][y] = ;
row[x][i] = col[y][i] = square[x/][y/][i] = ;
}
}
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 = ; i<; i++){
scanf("%s", mapp);
for(int j = ; j<; j++){
MAP[i][j] = mapp[j] - '';
int c = MAP[i][j];
if(MAP[i][j]) {
row[i][c] = col[j][c] = square[i/][j/][c] = ;
}
}
}
dfs();
for(int i=;i<;i++){
for(int j=;j<;j++)
printf("%d",MAP[i][j]);
printf("\n");
}
}
return ;
}

题目:poj 3074

思路:DLX是从数据结构角度优化01矩阵的精确覆盖和重复覆盖的一种数据结构;

   它用十字链表只存储矩阵中的非0元,而01矩阵精确覆盖dfs过程中矩阵会越来越稀疏而且每次恢复现场会浪费大量时间,DLX同时解决了这两个问题。

   本题关键在于将数独问题转化为01矩阵精确覆盖。

精确覆盖定义:

满足以下条件的集合为一个精确覆盖:

  • S*中任意两个集合没有交集,即X中的元素在S*中出现最多一次
  • S*中集合的全集为X,即X中的元素在S*中出现最少一次

合二为一,即X中的元素在S*中出现恰好一次。

举例:

令  = {NOEP} 是集合X = {1, 2, 3, 4}的一个子集的集合,并满足:

  • N = { }
  • O = {1, 3}
  • E = {2, 4}
  • P = {2, 3}.

其中一个子集 {OE} 是 X的一个精确覆盖,因为 O = {1, 3} 而 E = {2, 4} 的并集恰好是 X = {1, 2, 3, 4}。同理, {NOE} 也是 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* = {BDF} 便是一个精确覆盖。

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=(++)*+*+*****+;
#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+*);
int p=;
for(int i=;i<;i++)
{
for(int j=;j<;j++)
{
int num=O[p++];
//cout<<num<<endl;
num=num-(i*+j)*;
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+))
return true;
for(int j=L[i];j!=i;j=L[j])
resume(C[j]);
}
resume(c);
return false;
} void initDL(int n)
{
head=;
for(int i=;i<=n;i++)
{
U[i]=i;D[i]=i;
L[i]=i-;R[i]=i+;
S[i]=;
}
R[n]=;L[]=n;S[]=INF+;
sz=n+;
memset(H,,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[]; void build()
{
int p=;
initDL(**);
for(int i=;i<;i++)
for(int j=;j<=;j++,p++)
{
int base=(i*+j-)*;
if(str[p]=='.')
{
for(int k=;k<=;k++)
{
int r;
r=base+k;
//第i行有数字k
insert(r,i*+k);
//第j列有数字k
insert(r,*+(j-)*+k);
//第k块有数字k
int block=(j-)/*+i/;
insert(r,**+block*+k);
//第i行j列有一个数字(限制一个格子只填一个数)
insert(r,**+i*+j);
}
}
else
{
int k=str[p]-'';
int r=base+k;
//第i行有数字k
insert(r,i*+k);
//第j列有数字k
insert(r,*+(j-)*+k);
//第k块有数字k
int block=(j-)/*+i/;
insert(r,**+block*+k);
//第i行j列有一个数字(限制一个格子只填一个数)
insert(r,**+i*+j);
}
}
} int main()
{
size=; //9*9数独
while(~scanf("%s",str))
{
if(strcmp(str,"end")==)
break;
build();
dfs();
}
return ;
}

数独求解 DFS && DLX的更多相关文章

  1. 【位运算DFS/DLX】【HDU1426】【数独】

    题意:标准的一道数独题 DFS做法: 将横纵九宫格里的数字用位运算状态压缩,且可以通过逻辑或来确定总共有哪些数字被选择了,很方便也很快,代码如下 #include <cstdio> #in ...

  2. 编程之美之数独求解器的C++实现方法

    编程之美的第一章的第15节.讲的是构造数独.一開始拿到这个问题的确没有思路, 只是看了书中的介绍之后, 发现原来这个的求解思路和N皇后问题是一致的. 可是不知道为啥,反正一開始确实没有想到这个回溯法. ...

  3. 数独求解程序 php版

    数独求解程序 php版 <?php class Sudoku { var $matrix; function __construct($arr = null) { if ($arr == nul ...

  4. 经典数独游戏+数独求解器—纯C语言实现

    "心常乐数独小游戏"(下面简称"本软件")是一款windows平台下的数独游戏软件. 本软件是开源.免费软件. 本软件使用纯C语言编写,MinGW编译,NSIS ...

  5. C# 数独求解算法。

    前言 数独是一种有趣的智力游戏,但是部分高难度数独在求解过程中经常出现大量单元格有多个候选数字可以填入,不得不尝试填写某个数字然后继续推导的方法.不幸的是这种方法经常出现填到一半才发现有单元格无数可填 ...

  6. 数独求解问题(DFS+位运算优化)

    In the game of Sudoku, you are given a large 9 × 9 grid divided into smaller 3 × 3 subgrids. For exa ...

  7. HDU_1426——数独问题,DFS

    Problem Description 自从2006年3月10日至11日的首届数独世界锦标赛以后,数独这项游戏越来越受到人们的喜爱和重视. 据说,在2008北京奥运会上,会将数独列为一个单独的项目进行 ...

  8. POJ 2676/2918 数独(dfs)

    思路:记录每行每列每一个宫已经出现的数字就可以.数据比較弱 另外POJ 3074 3076 必须用剪枝策略.但实现较麻烦,还是以后学了DLX再来做吧 //Accepted 160K 0MS #incl ...

  9. 洛谷P1074 靶形数独【dfs】【剪枝】

    题目:https://www.luogu.org/problemnew/show/P1074 题意: 数独的分数如下.一个数独的总分数就是权值乘所填数字之和. 现在给一个未完成的数独,问分数最高的数独 ...

随机推荐

  1. 数据结构&算法-双向链表

    1.引言 双向链表原理和单链表差不多,就是操作相对繁琐一些,简单画了几个图,把思想弄清楚就好. 2.正文 这里介绍一下插入和删除的原理,相信这两个操作搞清楚的话,其他操作也没什么问题. 2.1插入节点 ...

  2. 移动设备页面高度不足时min-height 的尴尬处理

    移动设备页面高度不足时min-height 的尴尬处理 在做html5的页面时,经常遇到页面内容太少撑不起来整个手机屏幕的高度. 我们经常使用min-height来处理,比如min-height:56 ...

  3. 使用PHP导入和导出CSV文件

    我们先准备mysql数据表,假设项目中有一张记录学生信息的表student,并有id,name,sex,age分别记录学生的姓名.性别.年龄等信息. CREATE TABLE `student` ( ...

  4. Inside The C++ Object Model - 01

    前言 1.Foundation项目是一个定义大系统开发模型的项目,又叫Grail. 2.Grail中编译器被分为:parser(语法分析)->type checking -> simpli ...

  5. 自定义UITableViewCell

    随着日常的使用,系统提供的cell已经不能满足开发的需要,因为系统提供的是单一的,所以 这就引来了自定义cell的出现,可以根据 自己的需要来布局各个控件所处的位置.不同位置显示不同的控件. 创建一个 ...

  6. Linux_09------Linux上系统扫描和安全策略

    先谢慕课网/** * linux系统扫描技术 * * 主机扫描.路由扫描.批量服务扫描.系统安全策略(防SYN和ddos攻击) */ /** * 主机扫描 * ping fping hping * * ...

  7. sql良好习惯

    我们做软件开发的,大部分人都离不开跟数据库打交道,特别是erp开发的,跟数据库打交道更是频繁,存储过程动不动就是上千行,如果数据量大,人员流动大,那么我么还能保证下一段时间系统还能流畅的运行吗?我么还 ...

  8. linux 下文件节点索引

    最近发现一个奇怪的问题,就是一个pyhton 后台的服务一直打印日志文件,在中间我用vim看日志文件,关闭时习惯性的:wq退出,在此之后日志文件就不输出了. 1 对于这个现象我开始认为是python ...

  9. matlab squeeze函数的用法

    matlab中squeeze函数用于删除矩阵中的单一维(Remove singleton dimensions),比如执行下面的代码,随机产生一个1x2x3的矩阵A,然后squeeze(A)将返回一个 ...

  10. 构建ASP.NET网站十大必备工具(1)

    最近使用ASP.NET为公司构建了一个简单的公共网站(该网站的地址:http://superexpert.com/).在这个过程中,我们使用了数量很多的免费工具,如果把构建ASP.NET网站的必备工具 ...