方块填数算法详解

方块填数算法详解

1. 问题描述
数独”是当下炙手可热的智力游戏。一般认为它的起源是“拉丁方块”,是大数学家欧拉于1783年发明的。如图所示:6x6的小格被分为6个部分(图中用不同的颜色区分),每个部分含有6个小格(以下也称为分组)。开始的时候,某些小格中已经填写了字母(ABCDEF之一)。需要在所有剩下的小格中补填字母。全部填好后,必须满足如下约束:
在这里插入图片描述
为了表示上的方便,我们用下面的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
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的字母方阵,表示该解。
解的字母之间用空格分开。
如果找不到任何满足条件的解,则输出“无解”。
1.jpg
2. 算法分析

  • 首先根据已知条件将方块抽象出来,我们将它抽象为一个字符型二维矩阵,将已有字母填入二维矩阵,其余位置全部置#。将颜色记录到另一个整型二维数组中。
  • 其次根据约束条件写一个判断当前填入的字母是否符合约束条件的bool类型函数。(参考代码实现部分istrue函数)。
  • 然后写填数操作函数(灵魂所在),首先判断一下是不是已经全部填完了,即当前要填的位置已经超出二维矩阵最后一个元素下标位置,那么表示所有方格全部填完,输出当前二维矩阵的元素即完成题解。如果没有填完,就要进行填数操作了,判断当前方格内是否是#,如果不是表示当前方格内已经有字母则跳过当前方格,进行下一个方格填数。如果是#,在当前方格内依次判断字母A—F中有没有满足填入条件的字母,如果有便将字母填入,并继续向下一个方格内填数。如果没有符合条件的字母可填入到当前方格内,我们回溯到上一个方格,将方格首先重置为#再判断之前所填入字母之后所有字母有没有符合填入条件的,如果有则填入,并填下一个方格,如果没有则继续回溯前一个方格(参考代码实现部分dfs函数)。
    3. 代码实现
#include <stdio.h>
#include <stdlib.h>
#define N 6
int Grouparry[N][N];//描述方格颜色属性
char Datarry[N][N];//字符方格
char Zifu[N]={'A','B','C','D','E','F'};
int k=0;
int main()
{
    void InputData(int Grouparry[N][N],char Datarry[N][N]);
    void OutData(char Datarry[N][N]);
    void dfs(int x,int y);
    InputData(Grouparry,Datarry);//输入数据并初始化放方格
    dfs(0,0);//调用填数函数,初始位置为0,0
    return 0;
}
void InputData(int Grouparry[N][N],char Datarry[N][N])
{
    int i,j;
    int x,y;
    char ch;
   for(i=0;i<N;i++)
        for(j=0;j<N;j++)
        Datarry[i][j]='#';
    for(i=0;i<N;i++)
        for(j=0;j<N;j++)
        scanf("%d",&Grouparry[i][j]);
    scanf("%d",&j);
    for(i=0;i<j;i++)
    {
       scanf("%d%d%",&x,&y);
       scanf("%c",&ch);
       Datarry[x][y]=ch;
    }
}

void OutData(char Datarry[N][N])
{

    int i,j;
    for(i=0;i<N;i++)
    {
        for(j=0;j<N;j++)
            printf("%c\t",Datarry[i][j]);
        printf("\n");
    }

}
int istrue(int x,int y,char ch)
{
    int i,j;
    int logo=Grouparry[x][y];//记录当前方格的颜色
    for(i=0;i<N;i++)
    {
      if(Datarry[x][i]==ch||Datarry[i][y]==ch)//判断行列是否满足
      return 0;
      for(j=0;j<N;j++)
            if(Grouparry[i][j]==logo&&Datarry[i][j]==ch)//判断相同颜色内是否满足
            return 0;
    }
    return 1;
}
void dfs(int x,int y)
{
    int i;
    if(x>5)//如果所填入的方格行下标超过5则输出二维字符方格所有元素
    {
        k++;
        printf("%d\n",k);
        OutData(Datarry);
        return ;
    }
    if(Datarry[x][y]=='#')//如果不等于#则跳过当前方格
    {
        for(i=0;i<N;i++)
            if(istrue(x,y,Zifu[i]))
        {
            Datarry[x][y]=Zifu[i];//将字符填入当前方格
            dfs(x+(y+1)/6,(y+1)%6);//对下一个方格进行填数操作
            Datarry[x][y]='#';//回溯
        }
    }
    else
        dfs(x+(y+1)/6,(y+1)%6);

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值