Sudoku

Time Limit: 2000MS   Memory Limit: 65536K

题目链接http://poj.org/problem?id=2676

Description

Sudoku is a very simple task. A square table with 9 rows and 9 columns is divided to 9 smaller squares 3x3 as shown on the Figure. In some of the cells are written decimal digits from 1 to 9. The other cells are empty. The goal is to fill the empty cells with decimal digits from 1 to 9, one digit per cell, in such way that in each row, in each column and in each marked 3x3 subsquare, all the digits from 1 to 9 to appear. Write a program to solve a given Sudoku-task. 

Input

The input data will start with the number of the test cases. For each test case, 9 lines follow, corresponding to the rows of the table. On each line a string of exactly 9 decimal digits is given, corresponding to the cells in this line. If a cell is empty it is represented by 0.

Output

For each test case your program should print the solution in the same format as the input data. The empty cells have to be filled according to the rules. If solutions is not unique, then the program may print any one of them.

Sample Input

1
103000509
002109400
000704000
300502006
060000050
700803004
000401000
009205800
804000107

Sample Output

143628579
572139468
986754231
391542786
468917352
725863914
237481695
619275843
854396127

这题就是个最基本的数独,没有任何要求,只要求一个解就好了,其中0为应该填充的数字,这题博主是刚开始学深搜的时候写的吧。。。挺基础的,我们只需要维护行,列,块的出现的数字就行了,接下来就是正常的深搜了,一个格子一个格子慢慢搜过去。

行和列比较容易判断,块的话我们直接写个函数就好了:

int block(int a,int b) {if (a<=3) {if (b<=3) return 1;else if (b<=6) return 2;else return 3;} else if (a<=6) {if (b<=3) return 4;else if (b<=6) return 5;else return 6;} else {if (b<=3) return 7;else if (b<=6) return 8;else return 9;}
}

其中a,b就是x,y坐标

接下来就是简单粗暴的搜索了,从1,1搜到10,1,这个时候,行坐标已经超出了,我们就已经得到一个完整的数独了,记录一下然后全部退出就好了,当然,对于只要一个解的,我们要打个标记,让他找到ans之后不要继续回溯搜索了,不然有点浪费时间,可能会导致TLE。这一题比较水,不用怎么优化都能过,所以并不是我们要讲的重点,所以就贴一下当年有点难看的代码QAQ:

#include <cstdio>
#include <cstring>
int row[10][10],c[10][10],s[10][10],phot[10][10],mark;
int block(int a,int b);
void dfs(int r,int cl);
int main() {int n;scanf ("%d",&n);while (n) {n--;mark=0;memset(row,0,sizeof(row));memset(c,0,sizeof(c));memset(s,0,sizeof(s));memset(phot,0,sizeof(phot));for (int i=1; i<=9; i++)for (int j=1; j<=9; j++) {int ch;ch=getchar();while (ch<'0' || ch>'9') ch=getchar();phot[i][j]=ch-'0';if (phot[i][j]) {row[i][phot[i][j]]=1;c[j][phot[i][j]]=1;s[block(i,j)][phot[i][j]]=1;}}dfs(1,1);for (int i=1; i<=9; i++) {for (int j=1; j<=9; j++)printf ("%d",phot[i][j]);printf ("\n");}}return 0;
}
int block(int a,int b) {if (a<=3) {if (b<=3) return 1;else if (b<=6) return 2;else return 3;} else if (a<=6) {if (b<=3) return 4;else if (b<=6) return 5;else return 6;} else {if (b<=3) return 7;else if (b<=6) return 8;else return 9;}
}
void dfs(int r,int cl) {if (cl>9) cl=1,r++;if (r>9) {mark=1;return;}if (mark) return;while (phot[r][cl]) {cl++;if (cl>9) dfs(r+1,1);}for (int j=1; j<=9; j++) {if (mark) return;else if (!phot[r][cl] && !row[r][j] && !c[cl][j] && !s[block(r,cl)][j]) {phot[r][cl]=j;row[r][j]=1;c[cl][j]=1;s[block(r,cl)][j]=1;dfs(r,cl+1);if (mark) return;row[r][j]=0;c[cl][j]=0;s[block(r,cl)][j]=0;phot[r][cl]=0;} }
}

接下来就是我们的有点难度的靶型数独了:

P1074 靶形数独

题目链接https://www.luogu.org/problem/P1074

时间限制1.00s

内存限制125.00MB

题目描述

小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低。但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教,Z 博士拿出了他最近发明的“靶形数独”,作为这两个孩子比试的题目。

靶形数独的方格同普通数独一样,在 9 格宽×9 格高的大九宫格中有9 个 3 格宽×3 格高的小九宫格(用粗黑色线隔开的)。在这个大九宫格中,有一些数字是已知的,根据这些数字,利用逻辑推理,在其他的空格上填入 1 到 9的数字。每个数字在每个小九宫格内不能重复出现,每个数字在每行、每列也不能重复出现。但靶形数独有一点和普通数独不同,即每一个方格都有一个分值,而且如同一个靶子一样,离中心越近则分值越高。(如图)

上图具体的分值分布是:最里面一格(黄色区域)为 10 分,黄色区域外面的一圈(红色区域)每个格子为9分,再外面一圈(蓝色区域)每个格子为8 分,蓝色区域外面一圈(棕色区域)每个格子为7分,最外面一圈(白色区域)每个格子为6分,如上图所示。比赛的要求是:每个人必须完成一个给定的数独(每个给定数独可能有不同的填法),而且要争取更高的总分数。而这个总分数即每个方格上的分值和完成这个数独时填在相应格上的数字的乘积的总和

总分数即每个方格上的分值和完成这个数独时填在相应格上的数字的乘积的总和。如图,在以下的这个已经填完数字的靶形数独游戏中,总分数为 2829。游戏规定,将以总分数的高低决出胜负。

由于求胜心切,小城找到了善于编程的你,让你帮他求出,对于给定的靶形数独,能够得到的最高分数。

输入格式

一共 9 行。每行9个整数(每个数都在 0−9 的范围内),表示一个尚未填满的数独方格,未填的空格用“0”表示。每两个数字之间用一个空格隔开。

输出格式

输出共 1 行。输出可以得到的靶形数独的最高分数。如果这个数独无解,则输出整数−1。

输入输出样例

输入 

7 0 0 9 0 0 0 0 1
1 0 0 0 0 5 9 0 0
0 0 0 2 0 0 0 8 0
0 0 5 0 2 0 0 0 3
0 0 0 0 0 0 6 4 8
4 1 3 0 0 0 0 0 0
0 0 7 0 0 2 0 9 0
2 0 1 0 6 0 8 0 4
0 8 0 5 0 4 0 1 2

输出 

2829

输入 

0 0 0 7 0 2 4 5 3
9 0 0 0 0 8 0 0 0
7 4 0 0 0 5 0 1 0
1 9 5 0 8 0 0 0 0
0 7 0 0 0 0 0 2 5
0 3 0 5 7 9 1 0 8
0 0 0 6 0 1 0 0 0
0 6 0 9 0 0 0 0 1
0 0 0 0 0 0 0 0 6

输出 

2852

。。。。中文题,就不讲题意了,这题如果按照之前的暴力写的话会T到怀疑人生,很显然,它是要找到所有的解然后取最大的分数,那么接下来我们就开始我们的剪枝优化了,由于之前是无脑搜的,那么我们现在可以先找到一个搜索状态最少开始搜索,也就是说我们先判断每个空格的能够填的数字有多少个,然后记录状态最少的那个的坐标,然后对它进行搜索:

int x,y,midrow=100;
for (int i=1; i<=9; i++) {for (int j=1; j<=9; j++) {if (mp[i][j]) continue;int yes=0;for (int k=1; k<=9; k++) {if (!row[i][k] && !col[j][k] && !cell[block(i,j)][k]) yes++;}if (yes<midrow) x=i,y=j,midrow=yes;}
}
for (int k=1; k<=9; k++) {if (!row[x][k] && !col[y][k] && !cell[block(x,y)][k]) {row[x][k]=1;col[y][k]=1;cell[block(x,y)][k]=1;               mp[x][y]=k;dfs(cnt-1,goal+get_goal(x,y)*k);row[x][k]=0;col[y][k]=0;cell[block(x,y)][k]=0;      mp[x][y]=0;}
}

这里我们的dfs不再搜索所有格子,而是对空格数cnt进行搜索,当cnt==0的时候那么就也就说明搜索完毕,可以记录他的分值了。

然后在洛谷跑一下。。。。95分,T了一个点,然后最简单的就是手写个O2优化或者开O2了:

以下是AC代码:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
using namespace std;const int inf=1e9+10;int mp[12][12],row[12][12],col[12][12],cell[12][12];
int ans=-1;int get_goal(int x,int y);
int block(int x,int y);
void dfs(int cnt,int goal);int main()
{int cnt=0,goal=0;for (int i=1; i<=9; i++)for (int j=1; j<=9; j++){cin>>mp[i][j];if (mp[i][j]){goal+=mp[i][j]*get_goal(i,j);row[i][mp[i][j]]=1;col[j][mp[i][j]]=1;cell[block(i,j)][mp[i][j]]=1;}else cnt++;}dfs(cnt,goal);cout<<ans<<endl;return 0;
}int get_goal(int x,int y)
{if (x==1 || x==9 || y==1 || y==9) return 6;if (x==2 || x==8 || y==2 || y==8) return 7;if (x==3 || x==7 || y==3 || y==7) return 8;if (x==4 || x==6 || y==4 || y==6) return 9;return 10;
}int block(int x,int y)
{if (x<=3 && y<=3) return 1;if (x<=3 && y<=6) return 2;if (x<=3) return 3;if (x<=6 && y<=3) return 4;if (x<=6 && y<=6) return 5;if (x<=6) return 6;if (y<=3) return 7;if (y<=6) return 8;return 9;
}void dfs(int cnt,int goal)
{if (cnt==0) {ans=max(goal,ans);return;}int x,y,midrow=100;for (int i=1; i<=9; i++){for (int j=1; j<=9; j++){if (mp[i][j]) continue;int yes=0;for (int k=1; k<=9; k++){if (!row[i][k] && !col[j][k] && !cell[block(i,j)][k]) yes++;}if (yes<midrow) x=i,y=j,midrow=yes;}}for (int k=1; k<=9; k++){if (!row[x][k] && !col[y][k] && !cell[block(x,y)][k]) {row[x][k]=1; col[y][k]=1; cell[block(x,y)][k]=1;mp[x][y]=k;dfs(cnt-1,goal+get_goal(x,y)*k);row[x][k]=0; col[y][k]=0; cell[block(x,y)][k]=0;mp[x][y]=0;}}
}

但实际上我们一般是不能这么水过去(滑稽)。。。。

事实上这题需要二进制优化加速,我们将行,列,块的状态都保存为二进制,这个在之前的状压dp里面也讲过了二进制状压,应该不难理解。那么我们的维护数组就变成了row[9]  col[9]   cell[3][3],首先我们先初始化所有需要用到的数组:

int cnt=0,goal=0,x;
for (int i=0; i<9; i++) logg[1<<i]=i;
for (int i=0; i<(1<<9); i++)//枚举所有的填数的状态for (int j=i; j; j-=lowbit(j))ff[i]++;//对每个状态的有多少个数字进行预处理;
for (int i=0; i<9; i++) {row[i]=(1<<9)-1;col[i]=(1<<9)-1;cell[i/3][i%3]=(1<<9)-1;
}//初始化所有格子都可填,即用111111111填满所有维护的区域

那么对于每个格子所能填的状态为row[x] & col[y] & cell[x/3][y/3]  即每个维护状态的该值都能填。

接下来就是lowbit是使用了,lowbit是将这个数二进制状态下的最后一个1的大小返回,比如对于1100(二进制状态)返回4,那么我们对每个状态枚举的时候就直接扣掉它就是下一个状态了。那么对于dfs里面变化不大,只是做了个二进制的优化而已:

inline void dfs(int cnt,int goal){if (cnt==0){ans=max(goal,ans);return;}int x,y,ming=20;for(int i=0; i<9; i++){for (int j=0; j<9; j++){if (mp[i][j]) continue;int nb=ff[get_sta(i,j)];//所有可填状态的数量if (nb<ming) {ming=nb;x=i,y=j;}}}for (int i=get_sta(x,y); i; i-=lowbit(i)){int p=logg[lowbit(i)]+1;update(x,y,p);dfs(cnt-1,goal+get_goal(x,y)*p);update(x,y,-p);}
}

。。。。。看起来确实美观(高大上了许多)

以下是无吸氧AC代码:

#include <bits/stdc++.h>
using namespace std;int row[9],col[9],cell[3][3];
int ans=-1,mp[9][9];
int logg[1<<9],ff[1<<9];inline int get_goal(int x,int y){return min(min(x,y),min(8-x,8-y))+6;
}inline void update(int x,int y,int val){if (val>0) {val--;row[x]-=1<<val;col[y]-=1<<val;cell[x/3][y/3]-=1<<val;//将这些格子的这个值抹掉,即不可填mp[x][y]=val+1;}else {val=-val;val--;row[x]+=1<<val;col[y]+=1<<val;cell[x/3][y/3]+=1<<val;mp[x][y]=0;}
}inline int lowbit(int x){return x&-x;
}inline int get_sta(int x,int y){return row[x]&col[y]&cell[x/3][y/3];
}inline void dfs(int cnt,int goal){if (cnt==0){ans=max(goal,ans);return;}int x,y,ming=20;for(int i=0; i<9; i++){for (int j=0; j<9; j++){if (mp[i][j]) continue;int nb=ff[get_sta(i,j)];//所有可填状态的数量if (nb<ming) {ming=nb;x=i,y=j;}}}for (int i=get_sta(x,y); i; i-=lowbit(i)){int p=logg[lowbit(i)]+1;update(x,y,p);dfs(cnt-1,goal+get_goal(x,y)*p);update(x,y,-p);}
}int main()
{int cnt=0,goal=0,x;for (int i=0; i<9; i++) logg[1<<i]=i;for (int i=0; i<(1<<9); i++)//枚举所有的填数的状态for (int j=i; j; j-=lowbit(j))ff[i]++;//对每个状态的有多少个数字进行预处理;for (int i=0; i<9; i++) {row[i]=(1<<9)-1;col[i]=(1<<9)-1;cell[i/3][i%3]=(1<<9)-1;}//初始化所有格子都可填,即用111111111填满所有维护的区域for (int i=0; i<9; i++)for (int j=0; j<9; j++){cin>>x;if (x) {goal+=x*get_goal(i,j);update(i,j,x);}else cnt++;}dfs(cnt,goal);cout<<ans<<endl;return 0;
}

有趣的题目:简单深搜之数独与靶型数独--二进制状压加速与dfs数独剪枝相关推荐

  1. Oil Deposits(简单深搜)(又名: 僵尸王子的复仇计划)

    题目连接: 僵尸王子的复仇计划 题目: 在第一次植物僵尸世界大战中,植物国的黑玫瑰王子使用了植物国的超超超超级无敌禁术-----"BUG",开启了异次元的大门,在一位超超超超-级* ...

  2. 简单深搜(poj 3009)

    题目链接:http://poj.org/problem?id=3009 题目:冰壶撞向目的地,只有遇到"1"才能停下来,并且把"1"撞成"0" ...

  3. 简单深搜广搜基本模板

    简单搜索 DFS: 剪枝,条件 容易超时,超时后基本就是剪枝的问题/无限递归?,或者用广搜试试? 模板(自己的理解) int n,m;//一般输入的行列数/边界 int mov[4][2] = {1, ...

  4. dfs算法题目(深搜思想训练)

    迷宫游戏 我们用一个二维的字符数组来表示前面画出的迷宫: 其中字符S表示起点,字符T表示终点,字符*表示墙壁,字符.表示平地.你需要从S出发走到T,每次只能向上下左右相邻的位置移动,不能走出地图,也不 ...

  5. nyoj-491--幸运三角形--简单深搜枚举(TLE)

    题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=491 悲剧啊,TEL了 #include<stdio.h> #include& ...

  6. [HDU] 2553 N皇后问题-简单深搜

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2553 方法: 1.可以用对称的思想,即:如果N是偶数,则只计算第一个皇后分别放在第一行的位置1到N/ ...

  7. 第三道深搜-----------hdu1016

    简单深搜.每次对第step个位置进行测试, for(i=2;i<=n;i++) { if(第i个点没有被访问过,并且和为质数) result[step]=i:   //则将这个数放入step的位 ...

  8. java dfs_Java数据结构与算法 深搜(DFS)的简单使用(一)之排列组合

    今天,我们来简单介绍一下深度优先搜索(DFS)的概念和使用. 在百度词条中,对深搜的解释是这样的. 百度词条中的解释 由此,我们可知,深搜是广泛运用到 图 中的搜索方法之一. 用深度优先搜索遍历图的基 ...

  9. 算法之路——深搜、广搜(简单搜索)

    搜索 通过一定的顺序,枚举每一个数据(经常会通过一些判断条件去掉无意义的数据,即剪枝),找到想要的数据的过程. 深度优先搜索(dfs) 深度优先搜索属于图算法的一种,是一个针对图和树的算法,应为缩写为 ...

最新文章

  1. oracle imp使用
  2. Spring @Autowired 注入为 null
  3. hql可以使用distinct吗_香薰精油可以当香水使用吗
  4. 【杂谈】篇篇精华,有三AI不得不看的技术综述(超过100篇核心干货)
  5. Android.mk文件的解析
  6. gh0st源码分析与远控的编写(二)
  7. About SCCM 2012 UDA(User Device Affinity)
  8. [转]微信小程序登录逻辑梳理
  9. raspberry nas_使用Raspberry Pi NAS托管您自己的云
  10. 如何快速取消svn的关联
  11. 混迹职场,你的领导也是你的资源
  12. 我国计算机网络发展水平,计算机网络发展
  13. Symantec Backup Exec 2012修改显示语言
  14. 错误的SQL脚本,错误消息 4104
  15. 语音邮件 voice mail 概述
  16. 别被漫画骗了..棋魂真正的结局是....
  17. uniapp自定义整包更新与热更新
  18. 护眼灯真能护眼吗?学习专用的护眼灯推荐
  19. python a股行情_用Python,tushare做一个A股每日收盘行情监测分析(含源代码)
  20. 昆明php工作前景,学习php语言有前途吗 昆明计算机学校

热门文章

  1. 各大搜索引擎seo的区别
  2. 运营干货| 用户触达36计,和用户来次亲密接触
  3. 【软考八】软件设计师下午题试题三(刷题刷题)
  4. history源码解析-管理会话历史记录
  5. sofa server端处理client端请求流程
  6. 计算机的背景怎么找不到了,一个电脑背景图片,在所有可能存放位置都找不到,也删除不掉!...
  7. 智慧园区基础设施数字化管理,围绕园区需求打造绿色园区生态
  8. 在浏览器访问不到在阿里云购买服务器的公网ip的解决历程
  9. 什么是alpha matting?
  10. 简述什么是 Cloud Native 1