华为OJ-数独(C语言、递归)

 

问题描述:

数独(Sudoku)是一款大众喜爱的数字逻辑游戏。玩家需要根据9X9盘面上的已知数字,推算出所有剩余空格的数字,并且满足每一行、每一列、每一个3X3粗线宫内的数字均含1-9,并且不重复。

例如:(数组里空缺的地方用0表示)

输入:

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

输出:

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

思想:

(本方法默认输入输出都是二维数组。)

用两个数组row[]和col[],在输入时,分别记录下“0”的横纵坐标,zero代表“0”的个数。

利用递归的方法,把每一个空按照1~9的顺序填进去,填完后检查这个数在此处的合理性,如:同行、列之内,还有所处的小方阵中,不可以出现与填进去的数字相同的数字。

如果合理,则继续向下一个空尝试,如果不合理则尝试下一个数字。

如果1~9中都没有合适的数字可以填,说明前面有填错的空,将此位置置0,返回。

函数中的z代表现在是填的第几个空,每次填数之前要比较z和zero的大小,如果z > zero,说明已经都填满了,直接返回即可。

flag的意义:用来表示数独是否填满,如果递归返回时flag为1,说明数独已经满了,直接返回即可。如果flag为0,说明递归并未结束。

#include<stdio.h>

int num[9][9];
int flag = 0;
int check(int x, int y, int m)
{
    for(int i = 0; i < 9; i++)   //检查行同一行有没有这个数字
    {
        if(num[x][i] == m && i != y)
            return 0;    // 这个数已经存在
    }
    for(int i = 0; i < 9; i++)   //检查同一列有没有
    {
        if(num[i][y] == m && i != x)
            return 0;
    }
    int row = x / 3;
    int col = y / 3;
    //检查小方阵里是否存在
    for(int i = row * 3; i <= row * 3 + 2; i++)
    {
        for(int j = col * 3; j <= col * 3 + 2; j++)
        {
            if(num[i][j] == m && i != x && j != y)
                return 0;
        }
    }
    return 1;
}

void fill(int z, int * row, int * col, int zero)   //递归填数,z是现在是第几个0,zero是这个表格里0的总量
{
    if(z > zero)  //都填满了
    {
        flag = 1;
        return;
    }
    //开始填数
    for(int i = 1; i < 10; i++)
    {
        if(check(row[z - 1], col[z - 1], i) == 1)  //如果合理,就先用这个数继续填
        {
            num[row[z - 1]][col[z - 1]] = i;  //从1开始尝试
            fill(z + 1, row, col, zero);    
            if(flag)
                 return;
            //else的话,此处就继续++往后试
        }
    }
    //如果进行到这里,说明此处没合适的数,需要回溯
    num[row[z - 1]][col[z - 1]] = 0;   //将此处重新置0
    return;

}


int main()
{
    int i, j;
    int zero = 0;   //记录数组中0的个数
    int row[50] = { 0 };   // 两个数组分别记录0的横纵坐标
    int col[50] = { 0 };
    for(i = 0; i < 9; i++)
    {
        for(j = 0; j < 9; j++)
        {
            scanf("%d", &num[i][j]);
            if(num[i][j] == 0)
            {
                row[zero] = i;
                col[zero] = j;
                zero++;
            }
        }
    }
    int z = 1;  //从第一个0开始
    fill(z, row, col, zero);
    
    for(i = 0; i < 9; i++)
    {
        for(j = 0; j < 8; j++)
        {
            printf("%d ", num[i][j]);
        }
        printf("%d\n", num[i][8]);
    }
    return 0;
}

目前内存和时间都没有很好的实现,如果以后能有机会优化再说吧...能写出来已经知足了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值