方格填树

方格填数

如下的10个格子
在这里插入图片描述

填入0~9的数字。要求:连续的两个数字不能相邻。
(左右、上下、对角都算相邻)

一共有多少种可能的填数方案?

请填写表示方案数目的整数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

答案为:1580


典型的全排列+判断的问题。



解法一、

#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
int a[] = { 0,1,2,3,4,5,6,7,8,9 };
int ans = 0;
bool check() {
	if (
		abs(a[0] - a[1]) == 1 ||
		abs(a[0] - a[3]) == 1 ||
		abs(a[0] - a[4]) == 1 ||
		abs(a[0] - a[5]) == 1 ||

		abs(a[1] - a[2]) == 1 ||
		abs(a[1] - a[4]) == 1 ||
		abs(a[1] - a[5]) == 1 ||
		abs(a[1] - a[6]) == 1 ||

		abs(a[2] - a[5]) == 1 ||
		abs(a[2] - a[6]) == 1 ||

		abs(a[3] - a[4]) == 1 ||
		abs(a[3] - a[7]) == 1 ||
		abs(a[3] - a[8]) == 1 ||

		abs(a[4] - a[5]) == 1 ||
		abs(a[4] - a[7]) == 1 ||
		abs(a[4] - a[8]) == 1 ||
		abs(a[4] - a[9]) == 1 ||

		abs(a[5] - a[6]) == 1 ||
		abs(a[5] - a[8]) == 1 ||
		abs(a[5] - a[9]) == 1 ||

		abs(a[6] - a[9]) == 1 ||

		abs(a[7] - a[8]) == 1 ||

		abs(a[8] - a[9]) == 1)return false;		//若此排列中有一个元素与其相邻位置的元素连续即返回false
	return true;	//即此排列满足题意要求 
}

void fun(int k){  //递归回溯生成全排列的函数,数组中不能有重复元素 
	if(k==10) {  //递归边界,即已生成一种排列 
		if(check()){
		 ans++;   //若满足条件 ans++;
		} 
		return;
	}
	//k后面的数字都可以放在k位 
	for(int i=k;i<=9;i++){		
		int t = a[i];a[i]=a[k];a[k]=t;
		fun(k+1);	//递归 
		t = a[i];a[i]=a[k];a[k]=t; //回溯 
	}
}

int main() {
	fun(0);
	cout << ans;
	return 0;
}


使用C++的next_permutation函数实现全排列:

#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
int a[] = { 0,1,2,3,4,5,6,7,8,9 };//初始化数组
bool check() {
	//check()函数体与上个代码段相同
}
int main() {
	int ans = 0;
	do {
		if (check()) {    //对生成的排列进行检查
			ans++;		  //若符合题意要求ans++;
		}
	} while (next_permutation(a, a + 10)); //使用next_permutation函数生成a数组元素的全排列
	cout << ans;
	return 0;
}

若对next_permutation函数不熟悉可以查看相关的介绍:
https://so.csdn.net/so/search/all?q=next_permutation&t=all&p=1&s=0&tm=0&lv=-1&ft=0&l=&u=

解法二、

将把三行四列的格子声明为一个五行六列的二维数组、便于在判断10个格子的每个元素与相邻格子是否满足条件时,不需要考虑其边界情况。


#include <iostream>
#include <cmath>
using namespace std;
int a[5][6];
bool table[10]={0}; //判断数字0-9是否被使用 
int ans = 0;
void Init(){
	for(int i=0;i<5;i++){
		for(int j=0;j<6;j++){
			//将数组元素全部初始化为-10,以至于在判断0-9的格子周围格子时不受题意条件的影响 
			a[i][j] = -10; 
		}
	}
	
}
bool check(int x,int y) {
	//两层循环,判断a[x][y]与相邻的八个格子是否有连续的数字 
	 for(int i=x-1;i<=x+1;i++){
	 	for(int j=y-1;j<=y+1;j++){
	 		if(abs(a[i][j]-a[x][y]) == 1) return false; //若有一个连续的即返回false 
		 }
	 } 
	 return true; //满足题意条件,返回true 
}
void fun(int x,int y){
	if(x==3 && y==4){  //递归边界,已经生成一种满足题意的排列 
		ans++;
		return; 
	}
	for(int i = 0;i<=9;i++){
		if(table[i]==0){ //若i未使用,则进行操作 
			a[x][y] = i;  
			//填入一个数字即判断周围的格子是否有连续的,声明五行六列的二维数组的原因就在这里 
			if(!check(x,y)){ 
				a[x][y]=-10;
			 	continue;  //若i不满足题意条件,则跳过本次循环,判断i+1在此格子是否满足; 
			} 
			table[i]= 1;//满足check的条件,即i已被使用 
			if(y==4) fun(x+1,1);  //递归,列边界处理 
			else fun(x,y+1);
			{table[i] = 0;a[x][y] = -10;} //执行完一次递归即回溯 
		}
	}
} 
 
int main() {
	Init();
	fun(1,2); //从二维数组的1行2列开始处理 
	cout << ans << endl ;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值