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;
}