【编程题】(满分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)的更多相关文章

  1. Java 第十一届 蓝桥杯 省模拟赛 小明的城堡

    小明用积木搭了一个城堡. 为了方便,小明在搭的时候用的是一样大小的正方体积本,搭在了一个 n 行 m 列的方格图上,每个积木正好占据方格图的一个小方格. 当然,小明的城堡并不是平面的,而是立体的.小明 ...

  2. Java 第十一届 蓝桥杯 省模拟赛 梅花桩

    小明每天都要练功,练功中的重要一项是梅花桩. 小明练功的梅花桩排列成 n 行 m 列,相邻两行的距离为 1,相邻两列的距离也为 1. 小明站在第 1 行第 1 列上,他要走到第 n 行第 m 列上.小 ...

  3. Java 第十一届 蓝桥杯 省模拟赛 元音字母辅音字母的数量

    给定一个单词,请计算这个单词中有多少个元音字母,多少个辅音字母. 元音字母包括 a, e, i, o, u,共五个,其他均为辅音字母. 输入格式 输入一行,包含一个单词,单词中只包含小写英文字母. 输 ...

  4. Java 第十一届 蓝桥杯 省模拟赛 递增序列

    问题描述 在数列 a[1], a[2], -, a[n] 中,如果 a[i] < a[i+1] < a[i+2] < - < a[j],则称 a[i] 至 a[j] 为一段递增 ...

  5. Java 第十一届 蓝桥杯 省模拟赛 最大的元素距离

    在数列 a_1, a_2, -, a_n中,定义两个元素 a_i 和 a_j 的距离为 |i-j|+|a_i-a_j|,即元素下标的距离加上元素值的差的绝对值,其中 |x| 表示 x 的绝对值. 给定 ...

  6. Java 第十一届 蓝桥杯 省模拟赛 洁净数

    洁净数 小明非常不喜欢数字 2,包括那些数位上包含数字 2 的数.如果一个数的数位不包含数字 2,小明将它称为洁净数. 请问在整数 1 至 n 中,洁净数有多少个? 输入格式 输入的第一行包含一个整数 ...

  7. Java 第十一届 蓝桥杯 省模拟赛 第十层的二叉树

    一棵10层的二叉树,最多包含多少个结点? 注意当一棵二叉树只有一个结点时为一层. 答案提交 这是一道结果填空的题,你只需要算出结果后提交即可.本题的结果为一个整数,在提交答案时只填写这个整数,填写多余 ...

  8. Java 第十一届 蓝桥杯 省模拟赛 70044与113148的最大公约数

    问题描述 70044与113148的最大公约数是多少? 答案提交 这是一道结果填空的题,你只需要算出结果后提交即可.本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分. pac ...

  9. Java 第十一届 蓝桥杯 省模拟赛 19000互质的个数

    问题描述 不超过19000的正整数中,与19000互质的数的个数是多少? 答案提交 这是一道结果填空的题,你只需要算出结果后提交即可.本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将 ...

  10. Java 第十一届 蓝桥杯 省模拟赛十六进制转换成十进制

    问题描述 请问十六进制数1949对应的十进制数是多少?请特别注意给定的是十六进制,求的是十进制. 答案提交 这是一道结果填空的题,你只需要算出结果后提交即可.本题的结果为一个整数,在提交答案时只填写这 ...

随机推荐

  1. Jquery获得下拉框的值

    转自:http://blog.csdn.net/jing_xin/article/details/8007794 获取Select : 获取select 选中的 text : $("#ddl ...

  2. Zabbix 教程

    Zabbix 教程http://blog.csdn.net/linuxlsq/article/details/52606086 MySQL在以下几种情况会创建临时表:1.UNION查询:2.用到TEM ...

  3. SQL Server 2008维护计划 出错 无法实现自动备份

    ,MaintenancePlan.Subplan_1,错误,0,HBZGQ\TESTSQLSERVER,MaintenancePlan.Subplan_1,(作业结果),,该作业失败. 用户 sa 调 ...

  4. WCF学习心得----(五)生成客户端

    WCF学习心得----(五)生成客户端 1.    通过Svcutil.exe工具直接生成客户端 1.1     将服务承载于IIS上 1.1.1 在IIS中新建网站,所示效果如下图: 1.1.2   ...

  5. 如何设置DNS的SPF记录

    如何设置DNS的SPF记录 Introduction SPF的完整意思为 "Sender Policy Framework".翻译过来就是发送方策略框架,是一项跟 DNS 相关的技 ...

  6. WIN 8.1 x64 环境下 COM Surrogate 停止工作解决方案

    我的笔记本电脑是THINKPAD T420 4180AT8,NVIDIA NVS 4200M,Intel(R) HD Graphics 3000,WIN 8.1 x64操作系统.在安装完NVIDIA独 ...

  7. 黄聪:C#如何通过MeasureString、Graphics获取字符串的像素长度

    1.    使用g.MeasureString()获得 使用MeasureString测量出来的字符宽度,总是比实际宽度大一些,而且随着字符的长度增大,貌似实际宽度和测量宽度的差距也越来越大了.查了一 ...

  8. MVC 模型

    dbcontent var ALLALBUMS=from album in db.albums orderby album.title ascending select album; storeman ...

  9. DBA_Oracle AWR Report性能监控报表(案例)

    2014-08-22 Created By BaoXinjian

  10. python (3)简单语法:字符串(strip函数),数据类型

    一:字符串重复,索引,切片(字符串命令strip) 函数原型strip 声明:s为字符串,rm为要删除的字符序列 s.strip(rm)        删除s字符串中开头.结尾处,位于 rm删除序列的 ...