解数独_递归

前景提要:之前因为不想玩游戏把手游什么的删光了,回家前找了个数独的小游戏玩。分几个难度吧,但是在电脑面前都没什么难度,秒解,只是输入原数独比较麻烦。

百度了一下:编程 数独
找到这个博客,看了下代码,小改了下 约200多行
http://blog.csdn.net/happyanger6/article/details/43347711

撸代码:

#include <stdio.h>
#include <stdlib.h>
int count = 0; 

/*数独二维数组*/
int g_s[9][9] = {    
{0,0,0,5,0,0,0,6,0},
{8,0,9,0,0,0,0,1,0},
{1,6,0,0,8,7,0,0,0},
{3,0,0,0,2,6,0,0,0},
{0,0,7,0,1,0,6,0,0},
{0,0,0,8,5,0,0,0,3},
{0,0,0,4,7,0,0,2,1},
{0,4,0,0,0,0,9,0,8},
{0,8,0,0,0,3,0,0,0}
};

/*打印当前数独状态*/
int prt()
{
    int i = 0;
    int j = 0;
    for(i = 0;i < 9;i++)
    {
        for(j = 0;j < 9;j++)
        {
            printf(" %d ",g_s[i][j]);
        }
        printf("\n");
    }

    getchar();
}

/*获取一个位置当前所有可能的解*/ 
int get_all_num(int i,int j,int a[9]) //a数组用来保存所有可能解 
{
    int s[9] = {1,2,3,4,5,6,7,8,9};
    int row,col,k;

    /*删除当前行中已出现的值*/
    for(col = 0;col < 9;col++)
    {
        k = g_s[i][col];
        if(k != 0)
        {
            s[k-1] = 0;
        }
    }

    /*删除当前列中已出现的值*/
    for(row = 0;row < 9;row++)
    {
        k = g_s[row][j];
        if(k != 0)
        {
            s[k-1] = 0;
        }
    }

    /*删除当前九宫格中已出现的值*/
    row = (i/3)*3;
    col = (j/3)*3;
    for(i = row;i < (row+3);i++)
    {
        for(j = col;j < (col+3);j++)
        {
            k = g_s[i][j];
            if(k != 0)
            {
                s[k-1] = 0;
            }
        }
    }

    i = 0;
    for(k = 0;k < 9;k++)
    {
        if(s[k] != 0)
        {
            a[i] = s[k];
            i++;
        }
    }

    return i;
}

/*判断当前行是否合法*/
int check_row(int i,int num)   
{
    int j = 0;
    for(j = 0;j < 9;j++)
    {
        if(g_s[i][j] == num)
        {
            return 0;
        }
    }

    return 1;
}

/*判断当前列是否合法*/
int check_col(int j,int num)
{
    int i = 0;
    for(i = 0;i < 9;i++)
    {
        if(g_s[i][j] == num)
        {
            return 0;
        }
    }

    return 1;

}

/*判断当前九宫格是否合法*/
int check_block(int i,int j,int num)
{
    int row = (i/3)*3;
    int col = (j/3)*3;
    int k = 0;
    int l = 0;

    for(k = row;k < (row+3);k++)
    {
        for(l = col;l < (col+3);l++)
        {
            if(g_s[k][l] == num)
            {
                return 0;
            }
        }
    }


    return 1;
}

/*尝试一个解*/
int try_one(int i,int j,int num)
{
    if(check_row(i,num) && check_col(j,num) && check_block(i,j,num))
    {
        g_s[i][j] = num;
        //prt();
        return 1;
    }
    return 0;
}

/*获取下一个要填空的位置*/
int get_next(int *pi,int *pj)
{
    int i = *pi;
    int j = *pj;
    int r = i;
    int c = 0;
    j++;

    for(;r < 9;r++)
    {
        for(c = j;c < 9;c++)
        {
            if(g_s[r][c] == 0)
            {
                *pi = r;
                *pj = c;
                return 1;   
            }
        }
        j = 0;
    }

    if(r == 9)
    {
        return 0;
    }
    //下面这种情况不了解 感觉可以删了吧
    *pi = r;
    *pj = c;
    return 1;

}

/*找到一个解*/
void finish()
{
    printf("\n find a solution: \n");
    prt();
}

/*处理一个位置*/
void do_one(int i,int j)
{
    int row = i;
    int col = j;
    int n = 0;
    int k = 0;
    int a[9] = {0};
    count++;
    /*当前位置有解,下一个位置*/
    if(g_s[row][col] != 0) 
    {
        /*获取下一个无解的位置*/
        if(get_next(&row,&col))
        {
            /*对一下个位置递归操作*/
            do_one(row,col);
        }
        /*都有解了,成功*/
        else
        {
            finish();
        }

        /*当前位置有解,直接回溯*/
        return ;
    }

    /*当前位置无解*/
    else 
    {
        /*获取当前位置的所有可能解*/
        n = get_all_num(i,j,a); 

        for(k = 0;k < n;k++)
        {
            /*尝试所有可能的解,这里是重复操作,就不改了*/
            if(try_one(i,j,a[k]))
            {
                row = i;
                col = j;
                /*此位置找到合适的了,下一个*/
                if(get_next(&row,&col))
                {    
                    do_one(row,col);
                }
                /*当前位置已有解且没有下一个了,结束*/
                else
                {
                    finish();
                }
            }
        }

        /*要向前回溯,则这个位置找到的解无效,回溯前清0*/
        g_s[i][j] = 0;
        //prt();
        return;
    }

}

int main()
{
    prt();
    printf("\n");
    do_one(0,0);
    printf("count is :%d",count);
    return 0;

}

结果截图:
这里写图片描述

有两个地方可以再改简单一些:
1:判断在(i,j)这个地方num是否合法时,三个判断函数可以写成一个。
2:在do_one函数里,if那个条件可以不写(写的话,就是在(0,0)为非空的时候有用,感觉是这样),然后通过手动选择第一个为0为起始点去做递归。

解法相当暴力,用递归去解 。原本以为在解一些难度高的题的时候会运行慢一些,后来发现我想太多了。都是秒解的水平。在遇到某个空有多个可能解的时候,这里采用的办法是把所有可能解保存,然后逐一去试,如果遇到某个空无解,返回上一个有解的地方,用下一个可能解。(递归要学好 - -) 我觉得更好的解决办法是,先找到只有唯一解的地方,先把这些空填好了。(这样可以节约运算次数,然而在计算机现有的计算速度来看,没什么可节约的)也许把9*9的数独改成16*16的或者25*25的,就要考虑效率问题了。

来算算时间复式度,好吧我不会算。pass 大概是n方吧。

后来在想怎么设计一个只有唯一解的数独,想不到有什么好方法,还有怎么设置不同的难度。(显然和给的条件相关,但不是正比例)难道是在这随机几个点产生几个随机数,然后计算一下解的递归调用次数?留个坑,哪天想起来再挖。

参考:
C语言实现的数独解题程序
http://blog.csdn.net/happyanger6/article/details/43347711

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
数独问题可以使用回溯算法来决,不一定需要递归。下面是一个不使用递归数独算法: 1. 初始化一个空的9x9的数独矩阵,并将待填充的位置标记为0。 2. 创建一个栈stack,用于存储待填充的位置。 3. 找到数独矩阵中的一个空格(值为0),将其位置压入栈stack中。 4. 对于栈中的每个待填充位置,从1到9尝试填充数字,判断该位置是否符合数独规则,如果符合,则将该位置的值更新为填充的数字,并将该位置的所有相关位置(同行、同列、同宫)的候选数字集合更新。 5. 如果填充的数字与已有的数字冲突,或者无法填充数字,则回溯到上一个待填充位置,将该位置的值重新标记为0,并将其从栈stack中弹出。 6. 如果栈stack为空,则表示数独已经被成功填充,算法结束。 下面是一个Python代码实现: ```python def solve_sudoku(board): stack = [] for i in range(9): for j in range(9): if board[i][j] == 0: stack.append((i, j)) while stack: i, j = stack[-1] found = False for num in range(board[i][j] + 1, 10): if is_valid(board, i, j, num): board[i][j] = num update_candidates(board, i, j, num) stack.pop() found = True break if not found: board[i][j] = 0 update_candidates(board, i, j, 0) stack.pop() if not stack: return False i, j = stack[-1] return True def is_valid(board, i, j, num): for k in range(9): if board[i][k] == num or board[k][j] == num or board[(i//3)*3+k//3][(j//3)*3+k%3] == num: return False return True def update_candidates(board, i, j, num): for k in range(9): if board[i][k] == 0: board[i][k] = {1,2,3,4,5,6,7,8,9} - set(board[i]) - get_col(board, k) - get_box(board, i//3, k//3) if len(board[i][k]) == 1: board[i][k] = board[i][k].pop() if board[k][j] == 0: board[k][j] = {1,2,3,4,5,6,7,8,9} - set(board[k]) - get_col(board, j) - get_box(board, k//3, j//3) if len(board[k][j]) == 1: board[k][j] = board[k][j].pop() def get_col(board, j): return [board[i][j] for i in range(9)] def get_box(board, i, j): return [board[m][n] for m in range(i*3, (i+1)*3) for n in range(j*3, (j+1)*3)] ``` 其中,is_valid函数用于判断填充的数字是否符合数独规则;update_candidates函数用于更新每个位置的候选数字集合。如果候选数字集合只有一个元素,则直接填充该数字。 该算法的时间复杂度为指数级别,但是在实际应用中,由于数独中待填充的位置较少,因此算法的实际运行时间较短。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值