蓝桥杯---数独(模拟 || dfs)
【编程题】(满分33分)
“数独”是当下炙手可热的智力游戏。一般认为它的起源是“拉丁方块”,是大数
学家欧拉于1783年发明的。
如图[1.jpg]所示:6x6的小格被分为6个部分(图中用不同的颜色区分),每个部分含有6个小格(以下也称为分组)。
开始的时候,某些小格中已经填写了字母(ABCDEF之一)。需要在所有剩下的小格中补填字母。
全部填好后,必须满足如下约束:
1. 所填字母只允许是A,B,C,D,E,F 中的某一个。
2. 每行的6个小格中,所填写的字母不能重复。
3. 每列的6个小格中,所填写的字母不能重复。
4. 每个分组(参见图中不同颜色表示)包含的6个小格中,所填写的字母不能重复。
为了表示上的方便,我们用下面的6阶方阵来表示图[1.jpg]对应的分组情况(组号为0~5):
000011
022013
221113
243333
244455
445555
用下面的数据表示其已有字母的填写情况:
02C
03B
05A
20D
35E
53F
很明显,第一列表示行号,第二列表示列号,第三列表示填写的字母。行号、列号都从0开始计算。
一种可行的填写方案(此题刚好答案唯一)为:
E F C B D A
A C E D F B
D A B E C F
2011到2013蓝桥杯决赛题
F B D C A E
B D F A E C
C E A F B D
你的任务是:编写程序,对一般的拉丁方块问题求解,如果多解,要求找到所有解。
【输入、输出格式要求】
用户首先输入6行数据,表示拉丁方块的分组情况。
接着用户输入一个整数n (n<36), 表示接下来的数据行数
接着输入n行数据,每行表示一个预先填写的字母。
程序则输出所有可能的解(各个解间的顺序不重要)。
每个解占用7行。
即,先输出一个整数,表示该解的序号(从1开始),接着输出一个6x6的字母方阵,表示该解。
解的字母之间用空格分开。
如果找不到任何满足条件的解,则输出“无解”
例如:用户输入:
000011
022013
221113
243333
244455
445555
6
02C
03B
05A
20D
35E
53F
则程序输出:
1
E F C B D A
A C E D F B
D A B E C F
F B D C A E
B D F A E C
C E A F B D
再如,用户输入: 
001111
002113
022243
022443
544433
555553
7
04B
05A
13D
14C
24E
50C
51A
则程序输出: 
1
D C E F B A 
E F A D C B
A B F C E D
B E D A F C
F D C B A E
C A B E D F 
2
D C E F B A 
E F A D C B
A D F B E C
B E C A F D 
F B D C A E
C A B E D F 
3
D C F E B A
A E B D C F 
F D A C E B
B F E A D C 
E B C F A D
C A D B F E 
4
D C F E B A
B E A D C F
A D C F E B 
F B E A D C 
E F B C A D
C A D B F E 
5
D C F E B A 
E F A D C B
A B C F E D
B E D A F C 
F D B C A E
C A E B D F 
6
D C F E B A 
E F A D C B
A B D F E C
B E C A F D 
F D B C A E
C A E B D F 
7
D C F E B A 
E F A D C B
A D B F E C
B E C A F D 
F B D C A E
C A E B D F 
8
D C F E B A 
F E A D C B
A D B C E F
B F E A D C 
E B C F A D
C A D B F E 
9
D C F E B A 
F E A D C B
A F C B E D
B D E A F C 
E B D C A F
C A B F D E
【注意】
请仔细调试!您的程序只有能运行出正确结果的时候才有机会得分!
在评卷时使用的输入数据与试卷中给出的实例数据可能是不同的。
请把所有函数写在同一个文件中,调试好后,拷贝到【考生文件夹】下对应题号的“解答.txt”中即可。
相关的工程文件不要拷入。
源代码中不能使用诸如绘图、Win32API、中断调用、硬件操作或与操作系统相关的API。
允许使用STL类库,但不能使用MFC或ATL等非ANSI c++标准的类库。
例如,不能使用CString类型(属于MFC类库);例如,不能使用randomize, random函数(不属于ANSI C++标准)
开始的时候随手写了个代码,但是出乎意料的没有对,因为开始的时候直接按照两个方向开始深搜,但是这样会造成一个问题,就是左上角和右下角的可能就直接不走了,因为回溯的时候就把当时走的还原了,这样明显不适合将整个表格填满,并且最多能填17个,所以开始的时间有点浪费,但是还是记录下来,用作之后的比对用。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#define INF 0x3f3f3f3f
#define Maxsize 100
using namespace std;
string str[6];
char map[6][6]; struct T{
char all[6];
int len=0;
}choose[6][6]; struct noFill{
int row,col;
}notfill[36];//记录没有填充的坐标
int Case=0;
int Count=0;
void display()
{
printf("%d\n",++Case);
for(int i=0; i<6; i++)
{
for(int j=0; j<6; j++)
printf("%c ",map[i][j])<<' ';
printf("\n");
}
}
bool judge(char loca,char ch){//判断改行能否在该位置填充ch
for(int i=0;i<6;i++)
for(int j=0;j<6;j++)
if(str[i][j]==loca&&map[i][j]==ch)
return false;
return true;
}
bool judgeRow(int row,char ch){
for(int i=0;i<6;i++)
if(map[row][i]==ch)
return false;
return true;
}
bool judgeCol(int col,char ch){//判断在该小组里能否在该位置填充ch
for(int i=0;i<6;i++)
if(map[i][col]==ch)
return false;
return true;
}
int xxx=0;
void dfs(int tempcount){
if(tempcount==Count) {
display();
return;
}
int row=notfill[tempcount].row,col=notfill[tempcount].col;
char ch='A';
for(int i=0; i<6; i++) {//遍历该位置可以填充的字符
if( judgeRow(row,ch) && judgeCol(col,ch) && judge(str[row][col],ch)){
map[row][col]=ch;
dfs(tempcount+1);
}
ch++;
// xxx++;
}
map[row][col]='0';
return ;
}
int main(){
for(int i=0; i<6; i++)
cin>>str[i];
int n;
char in[5];
scanf("%d",&n);
memset(map,'0',sizeof(map));
for(int i=0; i<n; i++){
scanf("%s",in);
map[in[0]-'0'][in[1]-'0']=in[2];
}
for(int i=0;i<6;i++)
for(int j=0;j<6;j++)
if(map[i][j]=='0')
notfill[Count].row=i,notfill[Count++].col=j;
dfs(0);
// cout<<xxx<<endl;
return 0;
}
由于在向右向下深搜的时候回溯带来的重新还原的问题,这样,我就直接在开始的时候把没有填上的直接找出来,这样就可以一直深搜下去,即便回溯也可以重新经过该位置对其赋值,结果也就正确了
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#define INF 0x3f3f3f3f
#define Maxsize 100
using namespace std;
string str[6];
char map[6][6]; struct T{
char all[6];
int len=0;
}choose[6][6]; struct noFill{
int row,col;
}notfill[36];//记录没有填充的坐标
int Case=0;
int Count=0;
void display()
{
printf("%d\n",++Case);
for(int i=0; i<6; i++)
{
for(int j=0; j<6; j++)
printf("%c ",map[i][j])<<' ';
printf("\n");
}
}
bool judge(char loca,char ch){//判断改行能否在该位置填充ch
for(int i=0;i<6;i++)
for(int j=0;j<6;j++)
if(str[i][j]==loca&&map[i][j]==ch)
return false;
return true;
}
bool judgeRow(int row,char ch){
for(int i=0;i<6;i++)
if(map[row][i]==ch)
return false;
return true;
}
bool judgeCol(int col,char ch){//判断在该小组里能否在该位置填充ch
for(int i=0;i<6;i++)
if(map[i][col]==ch)
return false;
return true;
}
int xxx=0;
void dfs(int tempcount){
if(tempcount==Count) {
display();
return;
}
int row=notfill[tempcount].row,col=notfill[tempcount].col;
char ch='A';
for(int i=0; i<6; i++) {//遍历该位置可以填充的字符
if( judgeRow(row,ch) && judgeCol(col,ch) && judge(str[row][col],ch)){
map[row][col]=ch;
dfs(tempcount+1);
}
ch++;
// xxx++;
}
map[row][col]='0';
return ;
}
int main(){
for(int i=0; i<6; i++)
cin>>str[i];
int n;
char in[5];
scanf("%d",&n);
memset(map,'0',sizeof(map));
for(int i=0; i<n; i++){
scanf("%s",in);
map[in[0]-'0'][in[1]-'0']=in[2];
}
for(int i=0;i<6;i++)
for(int j=0;j<6;j++)
if(map[i][j]=='0')
notfill[Count].row=i,notfill[Count++].col=j;
dfs(0);
// cout<<xxx<<endl;
return 0;
}
感觉太暴力了不太好,所以又在遍历的时候首先处理了一下,在最开始的时候找出可能填在该位置的数找了一下,好像也没有节省多少时间,估计动态更新会好很多,就是每次填充上一个的时候,把该行,该列,改组里原来能填这个数的都删掉,但是好像不是很好处理。
#include <iostream>//这样做由于在回溯的时候把当初赋过的值又还原了,多以不能全部填满
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#define INF 0x3f3f3f3f
#define Maxsize 100
using namespace std;
string str[6];
char map[6][6]; struct T{
char all[6];
int len=0;
}choose[6][6]; struct noFill{
int row,col;
}notfill[36];
int Case=0;
int notfillcount=0;
void display()
{
printf("%d\n",++Case);
for(int i=0; i<6; i++)
{
for(int j=0; j<6; j++)
printf("%c ",map[i][j])<<' ';
printf("\n");
}
}
bool judge(char loca,char ch){
for(int i=0;i<6;i++)
for(int j=0;j<6;j++)
if(str[i][j]==loca&&map[i][j]==ch)
return false;
return true;
}
bool judgeRow(int row,char ch){
for(int i=0;i<6;i++)
if(map[row][i]==ch)
return false;
return true;
}
bool judgeCol(int col,char ch){
for(int i=0;i<6;i++)
if(map[i][col]==ch)
return false;
return true;
}
int xxx=0;
void dfs(int tempcount){
xxx++;
if(tempcount==notfillcount) {
display();
return;
}
int row=notfill[tempcount].row,col=notfill[tempcount].col;
for(int i=0; i<choose[row][col].len; i++) {//遍历该位置可以填充的字符
char ch=choose[row][col].all[i];
if( judgeRow(row,ch) && judgeCol(col,ch) && judge(str[row][col],ch)){
map[row][col]=ch;
dfs(tempcount+1);
}
}
map[row][col]='0';
return ;
}
void getChoose(){
for(int i=0;i<6;i++)
for(int j=0;j<6;j++)
if(map[i][j]=='0'){
notfill[notfillcount].row=i,notfill[notfillcount++].col=j;
char ch='A';
for(int x=0;x<6;x++){
if(judgeRow(i,ch)&&judgeCol(j,ch)&&judge(str[i][j],ch))
choose[i][j].all[ choose[i][j].len++ ]=ch;
ch++;
}
}
}
int main(){
for(int i=0; i<6; i++)
cin>>str[i];
int n;
char in[5];
scanf("%d",&n);
memset(map,'0',sizeof(map));
for(int i=0; i<n; i++){
scanf("%s",in);
map[in[0]-'0'][in[1]-'0']=in[2];
}
getChoose();
dfs(0);
cout<<xxx<<endl;
return 0;
}
网友代码(思路也挺好的,应该比我的更省时间)
上面的一种策略虽在开始的时候对每个位置能填的数先做了一下判断,但是在填数的时候还是不能动态的处理更新问题,下面的这种方式就很好的解决了这个问题,因为他每次都从最好填的位置开始填起的。。。
这个代码感觉运用空间的方式比较好,浪费掉几个“不值钱”的内存,处理起来却比较方便,并且还是运用近乎模拟的方式,每次从每行每列加起来的数字最多的位置开始填充,这样能够更节省时间,但是正常的处理也不会浪费太多时间,蓝桥杯还是几乎不需要优化的
#include<cstdio>//运用打表的优势
#include<iostream>
using namespace std;
int count=0 ; // 表示还剩余几个空没填
char map[6][6]={0};//存储结果,0表示结果矩阵中 当前位置还未填
int row[6]={0},col[6]={0};//统计行,列中元素个数
bool fillGro[6][6]={0}; // fillGrog[i][j] 表示分组为 i 的组中 'A'+j 是否使用过
char group[6][7];//记录输入时的分组情况
int Case = 0 ; //记录结果编号 void outPut(){
printf("%d\n",++Case);
for(int i=0;i<6;i++){
for(int j=0; j<5; j++)
printf("%c ",map[i][j]);
printf("%c\n",map[i][5]);
}
}
void searcher(){
int i,j,k,max,x,y;
bool f[6]={0};
if(count==36){
outPut();
return;
} count++;
max=-1 ;//寻找最佳位置---即当前没有填数的坐标所在行和所在列填数之和最多的位置
for(i=0;i<6;i++){
if(row[i]==6) // 当前行已经填满
continue;
for(j=0;j<6;j++)
if( map[i][j]==0 && (row[i]+col[j])>max ) { //每次找当前坐标所在行和所在列填数之和最多的 先填
max=row[i]+col[j];
x=i;
y=j;
}
}
//寻找所有可以填在最佳位置 (x, y) 的允许值
for(k=0;k<6;k++){
if(map[x][k])
f[ map[x][k]-'A' ] = true ;//
if(map[k][y])
f[ map[k][y]-'A' ] = true ;
if( fillGro[ group[x][y]-'0' ][k] )//k对应的字符(’A‘+k)
f[k]=true ;
}
row[x]++;
col[y]++;//当前的行和列都增加了一个
for(k=0;k<6;k++)
if(f[k]==false){
map[x][y] = 'A'+k;
fillGro[group[x][y]-'0'][k] = true ;
searcher();
fillGro[group[x][y]-'0'][k] = false ; // 回溯
}
// 回溯
row[x]--;//还原添加前的情景
col[y]--;
map[x][y] = 0 ;//还原图中未填状态
count--;
}
int main(){
char c;
int n,i,j;
for(i=0;i<6;i++)
scanf("%s",group[i]);
scanf("%d",&n);
c = getchar();
for( ; n ;n--){
i=getchar()-'0' ;
j=getchar()-'0' ;
c=getchar();//方式新颖
map[i][j] = c ;
count++;
row[i]++;
col[j]++;
fillGro[group[i][j]-'0'][c-'A'] = true ;
getchar();//吸收回车
}
searcher();
return 0;
}
写的有点多,最起码得对得起几个小时走弯路耗费的时间啊啊啊啊啊啊啊啊啊啊啊啊.......
蓝桥杯---数独(模拟 || dfs)的更多相关文章
- Java 第十一届 蓝桥杯 省模拟赛  小明的城堡
		
小明用积木搭了一个城堡. 为了方便,小明在搭的时候用的是一样大小的正方体积本,搭在了一个 n 行 m 列的方格图上,每个积木正好占据方格图的一个小方格. 当然,小明的城堡并不是平面的,而是立体的.小明 ...
 - Java 第十一届 蓝桥杯 省模拟赛 梅花桩
		
小明每天都要练功,练功中的重要一项是梅花桩. 小明练功的梅花桩排列成 n 行 m 列,相邻两行的距离为 1,相邻两列的距离也为 1. 小明站在第 1 行第 1 列上,他要走到第 n 行第 m 列上.小 ...
 - Java 第十一届 蓝桥杯 省模拟赛 元音字母辅音字母的数量
		
给定一个单词,请计算这个单词中有多少个元音字母,多少个辅音字母. 元音字母包括 a, e, i, o, u,共五个,其他均为辅音字母. 输入格式 输入一行,包含一个单词,单词中只包含小写英文字母. 输 ...
 - Java 第十一届 蓝桥杯 省模拟赛 递增序列
		
问题描述 在数列 a[1], a[2], -, a[n] 中,如果 a[i] < a[i+1] < a[i+2] < - < a[j],则称 a[i] 至 a[j] 为一段递增 ...
 - Java 第十一届 蓝桥杯 省模拟赛 最大的元素距离
		
在数列 a_1, a_2, -, a_n中,定义两个元素 a_i 和 a_j 的距离为 |i-j|+|a_i-a_j|,即元素下标的距离加上元素值的差的绝对值,其中 |x| 表示 x 的绝对值. 给定 ...
 - Java 第十一届 蓝桥杯 省模拟赛 洁净数
		
洁净数 小明非常不喜欢数字 2,包括那些数位上包含数字 2 的数.如果一个数的数位不包含数字 2,小明将它称为洁净数. 请问在整数 1 至 n 中,洁净数有多少个? 输入格式 输入的第一行包含一个整数 ...
 - Java 第十一届 蓝桥杯 省模拟赛 第十层的二叉树
		
一棵10层的二叉树,最多包含多少个结点? 注意当一棵二叉树只有一个结点时为一层. 答案提交 这是一道结果填空的题,你只需要算出结果后提交即可.本题的结果为一个整数,在提交答案时只填写这个整数,填写多余 ...
 - Java 第十一届 蓝桥杯 省模拟赛  70044与113148的最大公约数
		
问题描述 70044与113148的最大公约数是多少? 答案提交 这是一道结果填空的题,你只需要算出结果后提交即可.本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分. pac ...
 - Java 第十一届 蓝桥杯 省模拟赛 19000互质的个数
		
问题描述 不超过19000的正整数中,与19000互质的数的个数是多少? 答案提交 这是一道结果填空的题,你只需要算出结果后提交即可.本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将 ...
 - Java 第十一届 蓝桥杯 省模拟赛十六进制转换成十进制
		
问题描述 请问十六进制数1949对应的十进制数是多少?请特别注意给定的是十六进制,求的是十进制. 答案提交 这是一道结果填空的题,你只需要算出结果后提交即可.本题的结果为一个整数,在提交答案时只填写这 ...
 
随机推荐
- pt-heartbeat使用实践
			
今天无意中看到了沃趣网络科技的一篇文章请不要用SECONDS_BEHIND_MASTER来衡量MYSQL主备的延迟时间感觉分析的相当不错,该文章中也提到了使用pt-heartbeat这个工具检测主从延 ...
 - 自己理解的javascript 的对象和类理解
			
首先需要先理解类和对象的意义,我个人理解如下: 类:对象的抽象化: 对象:类的实体: javascript中没有class关键字和类的用法,只能用伪类来做类的,所以要用function来定义累的名字: ...
 - smarty缓存技术
			
后台: <?php //要求:当存在缓存文件,直接输出,不存在缓存文件,自己创建缓存,输出 //步骤: //定义该页面存放缓存文件的路径 $filename="../../cache/ ...
 - UIImageView的图片居中问题
			
原文链接地址:http://www.ganlvji.com/?p=139 原文链接地址:http://blog.csdn.net/zhoutao198712/article/details/87620 ...
 - Python(正则 Time datatime os sys random json pickle模块)
			
正则表达式: import re #导入模块名 p = re.compile(-]代表匹配0至9的任意一个数字, 所以这里的意思是对传进来的字符串进行匹配,如果这个字符串的开头第一个字符是数字,就代表 ...
 - ARM与x86之3--蝶变ARM
			
http://blog.sina.com.cn/s/blog_6472c4cc0100lqr8.html 蝶变ARM 1929年开始的经济大萧条,改变了世界格局.前苏联的风景独好,使得相当多的人选择了 ...
 - bzoj1803: Spoj1487 Query on a tree III
			
Description You are given a node-labeled rooted tree with n nodes. Define the query (x, k): Find the ...
 - perl语言书籍教程推荐
			
互动出版网计算机频道.为您推荐关于perl语言的书籍教程.包括perl push.perl chomp以及perl python等perl语言内容. perl语言书籍一.<Perl语言编程 第四 ...
 - PHP “Warning: session_start()...”、"correct (..\..\php5\Temp) in Unknown on line 0" 的解决方法
			
运行php的时候出现了一下警告: Warning: Unknown: open(D:/Program Files/php5/temp1\sess_l5b1a48m6kmb1g0t5cs33690v0, ...
 - elasticsearch2
			
简单认为是可以在命令行下访问url的一个工具 curl是利用URL语法在命令行方式下工作的开源文件传输工具,使用curl可以简单实现常见的get/post请求. curl -x 指定http请求的方法 ...