题目: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. 手把手教你接口自动化测试 – SoapUI & Groovy

    手把手教你接口自动化测试 – SoapUI & Groovy http://www.cnblogs.com/wade-xu/p/4236295.html 关键词:SoapUI接口测试,接口自动 ...

  2. The trash has reached its maximum size

    From: http://hi.baidu.com/aipie0066/item/1d7fb3e3a4710b3a4cdcaf5e The trash has reached its maximum  ...

  3. 纸上谈兵:表(list)

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 表 表(list)是常见的数据结构.从数学上来说,表是一个有序的元素集合.在C语言 ...

  4. PHP获取日期

    <?php echo "今天:".date("Y-m-d")."<br>"; echo "昨天:".d ...

  5. 通过案例对 spark streaming 透彻理解三板斧之一: spark streaming 另类实验

    本期内容 : spark streaming另类在线实验 瞬间理解spark streaming本质 一.  我们最开始将从Spark Streaming入手 为何从Spark Streaming切入 ...

  6. 【转】Java日期计算之Joda-Time

    Joda-Time提供了一组Java类包用于处理包括ISO8601标准在内的date和time.可以利用它把JDK Date和Calendar类完全替换掉,而且仍然能够提供很好的集成. http:// ...

  7. 理解docker容器和镜像(layer,ufs)和docker命令解释

    博客好文1:http://blog.csdn.net/x931100537/article/details/49633107(理解docker容器和镜像,理解简单,从原理入手,什么是layer,什么是 ...

  8. a + b + c 求和

    #include <iostream> int main() { std::cout << "请输入三个数字,以空格分隔,按回车键结束:" << ...

  9. linux配置的问题

    1 从系统设置-文本设置中把双拼删掉 2 通过sudo passwd root 修改root密码 3 通过su获取root权限 4 通过sudo pppoeconf输入宽带帐号密码 5 把更新源修改成 ...

  10. java异常和spring事务注解

    http://www.techferry.com/articles/spring-annotations.html http://www.oschina.net/question/2367675_23 ...