用回溯法解决填字游戏问题

刷题时刷到一道填字游戏问题,花了我不少时间,后来发现是自己写的回溯法有重复的情况(``汗)

分享一下我的解题想法:

题目描述:

在3*3个方格中要填入数字 1-10 的某九个数字,每个方格填一个整数,使所有相邻的两个方格内的整数之和为素数。编写一个实验程序,求出所有满足这个要求的数字填法。

众所周知,回溯法可以被用来找出一个问题满足条件的所有解。实际上,我们很难严格区分回溯法与深度优先遍历,甚至回溯法与蛮力法之间也没有十分清晰的分界线。

回溯法很多都是递归算法,而且在递归调用中隐含着状态的自动回退与恢复。

本题中要求求出所有解,那么应该保证解集“不重不漏”。那么在设计递归的时候可以指定递归的方向,保证每次得到的解都是唯一的(对于本题来说可以指定从每一行的左边递归至同一行的右边,当到达一行的右边界时,行数加一)。

话不多说,上代码:

//求解填字游戏问题
#include<iostream>
#include<cstring>
#define N 10//指定可以使用的数字范围为 1-10

using namespace std;

bool st[N+2];//状态数组,用于表示某个数是否被选取
int n = 3;
int cnt = 0;//记录所有填法的总和 N=10时为128种 N=12时为768种
int H[] = { 0,1,0,-1 };//水平分量
int V[] = { 1,0,-1,0 };//垂直分量


//判断一个数是否为素数
bool isPrime(int num) {
	if (num == 1) {
		return false;
	}
	for (int i = 2; i <= sqrt(num); i++) {
		if (num % i == 0) {
			return false;
		}
	}
	return true;
	
}

//打印一种填字结果
void display(int arr[][3]) {
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < 3; j++)
			cout << arr[i][j] << ' ';
		cout << endl;
	}
	cout << "----------------" << endl;
	cnt++;
}


//递归函数设计: dfs  传入待填字的数组  当前的横纵坐标(规定当前坐标已经填入了数字) 当前的层次(已经填入的数字的数量)
void dfs(int arr[][3], int x, int y,int lv) {


	for (int k = 0; k < 4; k++) {//检查四周的方格
		int newX = x + H[k];
		int newY = y + V[k];
		if (newX >= 0 && newX < n && newY >= 0 && newY < n) {
			if (arr[newX][newY] != 0 && !isPrime(arr[newX][newY] + arr[x][y])) {//不满足条件直接回溯
				return;
			}
		}
	}

	if (lv >= 9) {//出口
		display(arr);
		return;
	}

	if (y == n - 1) {//需要换行添加
		for (int i = 1; i <= N; i++) {
			if (!st[i]) {
				st[i] = true;
				arr[x + 1][0] = i;
				dfs(arr, x + 1, 0, lv + 1);
				st[i] = false;
				arr[x + 1][0] = 0;
			}
		}

	}
	else {//不需要换行添加
		for (int i = 1; i <= N; i++) {
			if (!st[i]) {
				st[i] = true;
				arr[x][y + 1] = i;
				dfs(arr, x, y + 1, lv + 1);
				st[i] = false;
				arr[x][y + 1] = 0;
			}
		}
	}



}

int main() {

	int a[3][3];
	memset(a, 0, sizeof a);
	memset(st, false, sizeof st);
	for (int i = 1; i <= N; i++) {//为arr初始化,先填入一个数字
		st[i] = true;
		a[0][0] = i;
		dfs(a, 0, 0,1);
		st[i] = false;
		a[0][0] = 0;
	}
	cout << "一共有 " << cnt << " 种填字方案" << endl;


	return 0;
}

能力有限,请各位大神多多指导呀。

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值