递归设计--八皇后+神奇的口袋+数列+吃糖果


2019.01.26 是日晴朗,纪念下放假以来第一次9点起,最近怎么越活越回去呢,别再患得患失啦,过好当下就行哟~


本文主要是递归设计练习的总结,包含以下内容(难度依次递减)

  • 八皇后(N皇后):全排列设计
  • 神奇的口袋(背包问题):动态规划或者穷举组合数
  • 数列(类似斐波那契):图形分解,找规律再打印
  • 吃糖果(类似斐波那契):直接修改斐波那契数列算法

问题 D: 八皇后

在这里插入图片描述
详细设计参考N皇后全解,在其基础上修改给n赋值为8即可
以下给出8皇后完整实现代码

完整Code

#include<cstdio>

const int maxn = 11;
int n=8,sum=0,P[maxn],hashTable[maxn]={false};
char ans[95][10];//存储所有全排列 

//判断两点是否在同一主/斜对角线上
//是-->true;否-->false 
bool isOk(int ix,int iy,int jx,int jy)
{
	//主、斜对角线 
	if(ix-jx == iy-jy || ix-jx == -iy+jy)return false;
	else return true;
}
//优化代码 
void generateP_Refined(int index)
{
	if(index == n+1){
		sum++;
		int i;
		for(i = 1; i <= n; i++){
			ans[sum][i-1] = P[i]+'0';
		}
		ans[sum-1][i-1] = '\0';
		return;
	}
	//全排列核心 
	for(int x = 1; x <= n; x++){
		if(!hashTable[x]){
			//优化判断,将x与1~index-1个数判断是否满足条件 
			bool bflag = true;//标记是否满足条件
			for(int i = 1; i < index; i++){
				if(!isOk(i,P[i],index,x)){//只要有一个不满足,立即跳出 
					bflag = false;
					break;//若有一个不满足,立刻退出 
				}
			}
			if(!bflag)continue;//尝试下一个数 
			
			P[index] = x;
			hashTable[x] = true;
			generateP_Refined(index+1);
			hashTable[x] = false;
		}
	}
}

int main()
{
	generateP_Refined(1);
	int a,b[a];
	scanf("%d",&a);
	for(int i = 0; i < a; i++){
		scanf("%d",&b[i]);
	}
	for(int i = 0; i < a; i++){
		printf("%s\n",ans[b[i]]);
	}
	return 0;
}

问题 C: 神奇的口袋

题目描述
有一个神奇的口袋,总的容积是40,用这个口袋可以变出一些物品,这些物品的总体积必须是40。John现在有n个想要得到的物品,每个物品的体积分别是a1,a2……an。John可以从这些物品中选择一些,如果选出的物体的总体积是40,那么利用这个神奇的口袋,John就可以得到这些物品。现在的问题是,John有多少种不同的选择物品的方式。
输入
输入的第一行是正整数n (1 <= n <= 20),表示不同的物品的数目。接下来的n行,每行有一个1到40之间的正整数,分别给出a1,a2……an的值
输出
输出不同的选择物品的方式的数目。
样例输入
2
12
28
3
21
10
5
样例输出
1
0


该问题实质是背包问题,动态规划的思想,递归实现(另一种思路可将所有组合列举出来,判断是否满足条件即可,较烦~)
每件物品仅有两种状态,选、不选
选了当前物品后,剩余空间减少,进行下一件物品取舍
不选当前物品,剩余空间不变,进行下一件物品取舍
以此导出递推表达式,以下为详细设计

递归设计

1.函数参数
  • i:当前背包标识(arr[i]表示i的大小,且假设i初始值为背包数n)
  • s:当前剩余空间
  • count:面对背包i,剩余空间s有多少种选择
2.递归边界
  • s=0:表示空间已满,满足条件可作为一种方案,返回1
  • i=1:若arr[i]恰好等于剩余空间s,返回1;否则返回0 (最后一件物品没得选,只有恰好才行)
3.递归执行

物品仅有两种状态:选,不选。若是i,s不满足以上边界条件,则需进行取舍

  • 若选择物品i,则下一次的计算中空间变为s-arr[i],物品编号变为i-1,继续计算 (count(arr,i,s)->count(arr,i-1,s-arr[i]))

  • 若不选物品i,则下一次的计算中空间不变,而物品编号变为i-1,继续计算 (count(arr,i,s)->count(arr,i-1,s))

  • 至此思路基本成型,不过咱们得精益求精。设想若是arr[i]>s,肯定不会选择i,所以可直接舍弃i,进行下一次计算(简单一点是一点)

完整Code(C/C++)

#include<cstdio>

int count(int arr[],int i,int s)
{
	//递归边界 
	if(s == 0)return 1;
	else if(i == 1){
		if(arr[i] == s)return 1;
		else return 0;
	}
	else if(arr[i] > s)return count(arr,i-1,s);//优化 
	else return count(arr,i-1,s)+count(arr,i-1,s-arr[i]);//递归执行 
} 
int main()
{
	int n;
	//输入多组数据测试 
	while(scanf("%d",&n) != EOF){
		int arr[n+1];
		for(int i = 1; i <= n; i++){
			scanf("%d",&arr[i]);
		}
		printf("%d\n",count(arr,n,40));
	}
	return 0;
 } 

问题 B: 数列

在这里插入图片描述


分析

该题类似斐波那契,但有些许不同:f(0)=0;f(1)=1;f(n)=f(n-1)+f(n-2),(n>1)
求解数列对大家来说轻而易举,关键在于打印图形,打印关键在于分而治之
打印的字符共两种:空格,数字。所以可将空格与数字分开打印
打印就得找规律,所谓规律即是每一行打印个数与行数间的关系式
这里假设行列值均从0开始且打印n行(具体规律交给聪明的你啦~)

完整Code

#include<cstdio>
//类似斐波那契:f(0)=0;f(1)=1;f(n)=f(n-1)+f(n-2),(n>1) 
int f(int n)
{
	if(n == 0)return 0;
	else if(n == 1)return 1;
	else return f(n-1)+f(n-2);
}
//打印图形时将其分为空格与数字两部分
//找到各自与行数间的关系式即可 
int main()
{
	int N,n,t[100];
	scanf("%d",&N);
	for(int k = 0; k < N; k++){
		scanf("%d",&t[k]);
	}
	//N组测试数据 
	for(int k = 0; k < N; k++){
		n = t[k];
		//打印n行 
		for(int i = 0; i < n; i++){
			//打印空格 
			for(int j = 0; j < 2*(n-i-1); j++){
				printf(" ");
			}
			//依次打印数字 
			for(int j = 0; j < 2*i+1; j++){
				printf("%d ",f(j));
			}
			printf("\n");
		}
	}
	return 0;
} 

问题 A: 吃糖果

在这里插入图片描述
类似斐波那契数列:f(1)=1;f(2)=2;f(n)=f(n-1)+f(n-2),(n>2)

完整Code

#include<cstdio>
//类似斐波那契数列 
int func(int n)
{
	if(n == 1)return 1;
	else if(n == 2)return 2;
	else return func(n-1)+func(n-2);
}
int main()
{
	int N,t[25],sum=0;
	//多组输入 
	while(scanf("%d",&N) != EOF){
		t[sum++] = N;
	}
	for(int i = 0; i < sum; i++){
		printf("%d\n",func(t[i]));
	}
	return 0;
} 
  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值