在练习算法的时候,看见了这个题,利用递归回溯的方法,解开了这个题
问题描述:
方格填数
如下的10个格子
填入0~9的数字。要求:连续的两个数字不能相邻。
(左右、上下、对角都算相邻)
一共有多少种可能的填数方案?
请填写表示方案数目的整数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。
以上就是问题的描述,第一感觉是枚举,但是想想,枚举可能超时,而且不好写,进而转变为其他的思路。
我们可以将这个问题分解为规模更小的子问题。例如已经填好了k个格子,在k个格子的基础上,我们进行处理问题,这样很容易就想到了递归的方法。
我们首先将格子规范化为下图这样
这样做的好处是,在判断当前数字和周围数字是否相邻的时候,可以采取统一的操作,即遍历以其自己为中心的9个方格,当然在遍历到其自身的时候跳过。
多增加的格子里的数字和所填的数字始终不会相邻,这样就不会对结果造成影响,而且可以规范判断时的操作。
以下是完整代码,代码后有注释
#include <iostream>
#include <math.h>
using namespace std;
int a[5][6];//开辟数组,为了保持统一性,在原来的方格的上下左右都新添了一行
bool num[10];//存放0-9这个10个数字的使用情况
int p=0;//记录方法数
bool flag = true;//标记当前的填法是否成功
void judge(int i,int j)
{
if (i == 3 && j == 4)//当填到第a[3][4]的时候,成功,因为图中的格子只到a[3][3]
{
p++;
return;
}
for (int k=0;k<=9;k++)//遍历10个数字
{
flag = true;
if (num[k]==false)//当前的数字没有被使用
{
a[i][j] = k;//令当前的这个格子的数字为k
num[k]=true;//令这个数字为已使用状态
}
else
continue;//如果当前的数字已经使用了,那么跳过
for (int m = -1; m <= 1; m++)//如果当前的数字可以使用,那么检验自己周围的格子,看看是否满足条件,算上自己应该有9个格子,以自己为中心
{
for (int n = -1; n <= 1; n++)
{
if (m == 0 && n == 0)//如果要检查的格子为自身,直接跳过
continue;
if (abs(a[i][j] - a[i + m][j + n]) == 1)//绝对值相差为1,意味着为连续数字
{
num[k] = false;//当前使用的数字被还回去
flag = false;//当前方法不成功
}
}
}
if (flag == false)//如果当前方法不成功,那么继续尝试下一个数字
continue;
else//如果成功则继续下一个数字
{
if (j == 4)
{
judge(i + 1, 1);//当到达最右边的时候,重新进入下一行的考察
num[k] = false;//返回的时候,把占用的数字还回去,因为当返回的时候,意味以当前这个k延伸出去的所有解法已经没有
//所以要将当前的这个格子的数字调整为k+1,所以刚刚使用的k应该被还回去,所以设置k的状态为false未使用
}
else
{
judge(i, j + 1);//没有到达最右的时候,向右
num[k] = false;//返回的时候,把占用的数字还回去,道理同上
}
}
}
a[i][j] = -10;//当前这个格子的问题结束了,把格子置为未使用状态
}
int main() {
int i, j;
for (i=0;i<10;i++)//初始化数字状态数组
num[i] = false;
for (i=0;i<5;i++)//初始化格子,都是未使用状态,-10也可以是别的数字,只要别和0-9连续
for (j=0;j<6;j++)
a[i][j] = -10;
judge(1, 2);//从a[1][2]开始填
cout << p<<endl;//输出方法总数
cin >> p;
return 0;
}