九宫格数独--回溯法

数独顾名思义——每个数字只能出现一次。数独是一种源自18世纪末的瑞士,后在美国发展、并在日本得以发扬光大的数字谜题。数独盘面是个九宫,每一宫又分为九个小格。在这八十一格中给出一定的已知数字和解题条件,利用逻辑和推理,在其他的空格上填入1-9的数字。使1-9每个数字在每一行、每一列和每一宫中都只出现一次。 这种游戏全面考验做题者观察能力和推理能力,虽然玩法简单,但数字排列方式却千变万化,所以不少教育者认为数独是训练头脑的绝佳方式。

九宫格的游戏在很多地方都经常出现,比如手机,电脑等等,下面就以简单的一个例子来说明如何使用回溯算法来解决该类问题。下面数组代表最初给出的九宫格,0为空格需要填的数字。
int[,] pu = new int[9, 9]
        {
        {0,0,0,7,2,8,0,0,0},
        {0,9,0,0,5,1,6,0,0},
        {0,0,0,0,6,0,0,8,2},
        {3,0,0,8,0,2,7,0,4},
        {1,7,4,0,3,0,0,2,0},
        {2,8,0,5,0,0,0,3,0},
        {0,1,0,3,0,0,2,0,0},
        {0,0,7,0,4,6,0,0,5},
        {0,0,6,1,0,0,0,4,9} };
下面使用回溯算法来解决该类问题:
1.验证函数如下:
private bool IsValid(int i, int j)
        {
            int n = pu[i,j];
            int[] query=new int[9]{0,0,0,3,3,3,6,6,6};

            int t, u;
            for (t = 0; t < 9; t++)
            {
                if ((t != i && pu[t, j] == n) || (t != j && pu[i, t] == n))
                    return false;
            }

            for (t = query[i]; t < query[i] + 3; t++)
            {
                for (u = query[j]; u < query[j] + 3; u++)
                {
                    if ((t != i || u != j) && pu[t, u] == n)
                        return false;
                }
            }
            return true;

        }

2.显示函数:

private void Show()
       {
           int n=0;

           for (var i = 0; i < 9; i++)
           {
               for (var j = 0; j < 9; j++)
               {
                   Console.Write(pu[i, j] + " ");

               }
               Console.WriteLine();
           }
           Console.WriteLine("----------------------------------------------");

       }

3.使用回溯算法求解:

private void Try(int n)
       {
           if (n == 81) {//是否已经是最后一个格子
               Show();
               return;
           }

            int i = n / 9, j = n % 9;

           if (pu[i,j] != 0) {//如果当前格子不需要填数字,就跳到下一个格子
               Try(n + 1);
               return;
           }

           for (int k = 0; k < 9; k++) {
               pu[i,j]++;//当前格子进行尝试所有解
               if (IsValid(i, j))
                   Try(n + 1);//验证通过,就继续下一个
           }

           pu[i,j] = 0;  //如果上面的单元无解,就回溯
       }

4.调用如下:

public void Test()
        {
            Show();
            Try(0);
        }

实际在游戏中会有不同的游戏级别如:

低级:要求至少有一行或一列出题时已填上5个数据,其他可以随机安排,但是每行或每列必须有数据  
中级:要求至少有一行或一列出题时已填上4个数据,其他可以随机安排,但是每行或每列必须有数据  
高级:要求至少有一行或一列出题时已填上3个数据,其他可以随机安排,但是每行或每列必须有数据

所以实际中我们使用回溯不是很方便,我说下我的方法:

1.只要一个九宫格的任意两行,两列所在列有相同的数字,那么这两行,两列交叉后该九宫格就只剩下一格,这个格子就是前面的相同数字。

2.使用1的思想,如果一个九宫格任意两行一列,或两列一行所在行列都有相同数字,那么该九宫两行一列交叉后只剩两个格子,如果一个格子游戏已经给出
数字,剩下的格子就是该数字了。

3.一次类推,一行一列,0行1列等等。

时间: 2024-12-19 01:51:04

九宫格数独--回溯法的相关文章

回溯法、数独与N阶可达问题(一)

回溯法是剪了枝的穷举,这是字面上的说法,不太好理解,不如讲解实例来的酸爽,于是引出了N阶可达问题: 有N个国家,每个国家有若干城市,小明要从中国(任意一个城市)出发,遍历所有国家(假设这个遍历顺序已经定了),最终到达美利坚(任意一个城市).而城市之间有可能不可达,只有小明尝试过才知道(就是后面的check()函数),求满足要求的一条路径? 从上面的表述中我们已经嗅到了浓浓的穷举屌丝气质——遍历所有组合,但是我们的回溯思想总是基于这样一个简单的事实:如果当前选择导致你走进了死胡同,那么这个选择一定

回溯法求解数独算法(C语言)

没有对输入的待解数独进行一般性验证(同一行.一列以及同一个小九宫格都不能出现重复数字) 算法利用回溯的思想: 从第一个空白处开始,找到其候选解(排除同行.同列以及同一小九宫格的所有出现过的数字,剩下未出现的数字都是候选解)的第一个值填入数独. 对第二个空白执行第一步(前面所填入的数字对此空白处有影响). 当出现某个空白的候选解个数为0时,就开始回溯,找到第一个候选解多于一个的,将其在使用的候选解设为不可取(本程序取值为-1),找到其下一个候选解,继续上面的步骤! 直到所有空白处填满,运算完成,输

数独1--暴力回溯法(时间超)

数独1--暴力回溯法(时间超) 一.心得 可用暴力搜索法(找唯一数单元格)和Dancing Links算法求解 先回顾之前的三篇文章 "算法实践--数独的基本解法",介绍求解数独的基本的暴力搜索法 "跳跃的舞者,舞蹈链(Dancing Links)算法--求解精确覆盖问题",网友huangfeidian介绍的求解数独的舞蹈链(Dancing Links)算法,这篇文章是介绍舞蹈链(Dancing Links)算法的. "算法实践--舞蹈链(Dancing

求解数独回溯算法

实现的java代码如下: //判断a[i][j]取值val是否有效 public boolean isValid(int[][] a, int i, int j, int val){ //判断是否跟同行冲突 for(int j1=0;j1<9;j1++){ if(a[i][j1]==val) return false; } //判断是否跟同列冲突 for(int i1=0;i1<9;i1++){ if(a[i1][j]==val) return false; } //找出a[i][j]所在的九

python常用算法(7)——动态规划,回溯法

引言:从斐波那契数列看动态规划 斐波那契数列:Fn = Fn-1 + Fn-2    ( n = 1,2     fib(1) = fib(2) = 1) 练习:使用递归和非递归的方法来求解斐波那契数列的第 n 项 代码如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 # _*_coding:utf-8_*_ def fibnacci(n):     if n == 1

五大常用算法之四:回溯法

(转自:http://www.cnblogs.com/steven_oyj/archive/2010/05/22/1741376.html) 1.概念 回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径. 回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标.但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”. 许

回溯法 -数据结构与算法

1.回溯法算法思想: 定义: 回溯法(探索与回溯法)是一种选优搜索法,按选优条件向前搜索,以达到目标.但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”. 1.回溯法适用:有许多问题,当需要找出它的解集(全部解)或者要求回答什么解是满足某些约束条件的最优解时,往往要使用回溯法. 2.有组织的穷举式搜索:回溯法的基本做法是搜索或者有的组织穷尽搜索.它能避免搜索所有的可能性.即避免不必要的搜索.这种方

0-1背包-回溯法

算法描述: 0-1背包的回溯法,与装载问题的回溯法十分相似.在搜索解空间树时,只要其左儿子结点是一个可行结点,搜索就进入其左子树.当右子树中有可能包含最优解时才进入右子树进行搜索.否则将右子树剪去. 计算右子树上界的更好算法是: 将剩余物品依其单位重量价值排序,然后依次装入物品,直至装不下时,再装入该物品的一部分而装满背包. 算法实现: 由Bound函数计算当前节点处的上界. 类Knap的数据成员记录解空间树的节点信息,以减少参数传递及递归调用所需的栈空间. 在解空间树的当前扩展结点处,仅当要进

回溯法浅谈

回溯法是初学者学习暴力法的第一个障碍,所谓回溯就是指当把问题分成若干步骤并递归求解时,如果当前步骤没有合法选择,则函数将返回上一级递归调用,这种现象称为回溯.正是因为这个原因,递归枚举算法常被称为回溯法,应用十分普遍. 八皇后问题 1 int tot=0; 2 int c[maxn]; //c[x]表示x行所在的列数 3 void dfs(int n,int cur){ 4 if(cur==n) ++tot; 5 for(int i=0;i<n;i++){ 6 int flag=0; 7 c[c