2019.9.28 Phil的课堂笔记之递归

2019.9.28
!!!
本蒟蒻的第一篇上课笔记
今天又听了一遍递归

本课堂笔记目录

  1. 递归函数的定义与使用

  2. 汉诺塔问题

  3. 放苹果问题(可以为空)(luogu-P2386 放苹果

  4. 放苹果问题(不可以为空)(luogu-T98856 集合的划分

  5. 数的划分(P1025 数的划分

  6. 幂次方(P1010 幂次方

  7. P2239 螺旋矩阵(P2239 螺旋矩阵

0.0 递归函数的定义与使用

1.0 汉诺塔问题
这个游戏很有意思,没有玩过的小伙伴请移步7k7k或4399后看一下
(划水哈哈哈哈 goto---->7k7k新汉诺塔

汉诺塔
本蒟蒻只能用代码过关了qwq

本题思路:
  1. 如果有0个盘子,程序结束
  2. 第一步先把n-1个盘子借助c柱子移到b柱子上0
    1
  3. 第二步就把a柱上剩下的的那个盘子直接移到c柱上
    2
  4. 把b柱上的n-1个盘子借助a柱移到c柱上
    在这里插入图片描述

实现代码

方法1 递归
#include<iostream>
using namespace std;
int n,k=0;
void f(int n,char a,char c,char b){
	if(n==0)	return;//如果为0就退出 
	f(n-1,a,b,c);	   //借助c柱过渡 
	k++;			   //计数
	cout<<k<<" "<<a<<" -> "<<c<<endl;
	f(n-1,b,c,a); 		//用a柱协助将b上面的n-1转移到c柱上面
}
int m;
int main (){
	scanf("%d",&m);
	f(m,'a','c','b'); 
	return 0;
} 
方法2:通项
  	想想为什么
f(n)=pow(2,n)-1;
方法3:转化

来自同班同学的代码
先把每个盘子放1
再考虑为空的个数

#include<bits/stdc++.h>
using namespace std;
int apple(int m,int n){
    if(m==0||m==1||n==1)
    	return 1;
    else if(m<n)
    	return apple(m,m);
    else
    	return apple(m-n,n)+apple(m,n-1);
}
int n,m,nu;
int main(){
    scanf("%d%d",&m,&n);
    printf("%d\n",apple(m-n,n));
    return 0;
}

1.0 放苹果问题

题目
思路
本题是递归以及递推很经典的一道题
我们设f(int m,int n)为m个苹果放到n个盘子里面
这个我们分两种情况

  1. 当前位置不放苹果那么方案数为f(m,n-1)
    相当于把m个苹果压榨到了n-1个盘子里(空一个盘子给蔡徐坤)
  2. 第2种是该位置放苹果
    那么方案数为f(m-n,n)
    相当于每个盘子拿走一个方案数不变;
f(m,n)=f(m,n-1)+f(m-n,n)

故实现程序如下

#include<iostream>
using namespace std;
int f(int m,int n){
	if(m<n)			return f(m,m); 
	if(n==1)	return 1;
	if(m==0)	return 1;
	else return	f(m,n-1)+f(m-n,n);
}
int m,n,t;
int main(){
	cin>>t;
	for(int i =1 ; i<= t; i++){
		cin>>m>>n;
		printf("%d\n",f(m,n));
	}
	return 0;
}

2.0 放苹果问题(不能为空)

在这里插入图片描述
本题是上一题的变式
思路

我们设f(int n,int k)为m个苹果放到n个盘子里面(不能为空)
这个我们分两种情况

  1. 当前位置只放1那么方案数为f(n-1,k-1)
    相当于最后1个放1; n-1全部挤到前面的盘子里!
    (糟糕的特权!)
  2. 第2种是所有数都不为空,插进去一个数,那么根据乘法原理可得
    那么方案数为n*f(k-1,n)
    每个里面都有数,放一个数进去用乘法

实现代码

#include<iostream>
#include<cstdio>
using namespace std;
int ant (int k ,int n){				//n盘子 
	if(n==1||k==n)	return 1;		//边界
	else			return n*ant(k-1,n)+ant(k-1,n-1);
} 
int m,n;
int main(){
	cin>>m>>n;
	printf("%d",ant(m,n));
	return 0;
}

3.0 数的划分

在这里插入图片描述

方法一:递归
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int sum(int n,int k){
	
	if(n<k)		return 0;
	if(k==n||k==1)	return 1;
	return sum(n-1,k-1)+sum(n-k,k);
	}
int n,k;
int main(){
	cin>>n>>k;
	printf("%d",sum(n,k));
	return 0;
}

方法二:记忆化搜索

记忆化搜索!!!
注意
先存储,后返回
如果表中有,直接返回

#include<iostream>
#include<cstdio>
using namespace std;
int n,sum,h[1005];
int dfs(int x)
{
	if(h[x]) return h[x];	//判断是否在记忆中 
	int ans=1;				//进入他自己的贡献 
	for(int i=1;i<=x/2;i++)
	{
		int ss=dfs(i);
		ans+=ss;
	}
	h[x]=ans;		//先存储 
	return ans;		//再返回 
}
int main()
{
	scanf("%d",&n);
	h[1]=1;
	dfs(n);
	printf("%d",h[n]);
	return 0;
}

4.0 数的计算

在这里插入图片描述
实现程序

#include<iostream>
#include<cstdio>
using namespace std;
int a[1005],n,ans;
int main(){
	cin>>n;
	a[1]=1;
	for(int j = 2; j<= n; j++){
		a[j]=1;
		for(int i = 1; i <= j/2; i ++ )	a[j]+=a[i];	
	}
	printf("%d",a[n]);
	return 0;
}

5.0 幂次方

P1010 幂次方
在这里插入图片描述

方法一:用log2

//Author:PhilFan;
#include<iostream>
#include<cstdio>
#include<cmath> 
#include<cstring>
using namespace std;
void rec(int x,int k){
	if(k==0)	{cout<<2<<"(0)";return;}
	if(k==1)	cout<<2;
	if(k>=2){
		cout<<2<<"("; 
		rec(k,log2(k));
		cout<<")";
	}
	int m = x-pow(2,k);
	if(m>0){
		cout<<"+";
		rec(m,log2(m));
	}
}
int main()
{
	int n;
	cin>>n;
	if(n==0){
		cout<<0; return 0;
		
	}
	int k = log2(n);
	rec(n,k); 

	return 0;
}

方法二:用log再换底

#include<bits/stdc++.h>
using namespace std;
int n;
void f(int n){
	if(n==1) {
		printf("2(0)");return;
	}
	if(n==2){
		printf("2");return;
	} 
	if(n==3){
		printf("2+2(0)");return;
	}								//定义边界
	
	int p=log(n)/log(2);			//换底公式 我数学好烂
	printf("2(");f(p);printf(")");	//把第一个加数也就是2的n次最大的,变成幂次方的形式
	//递归中间数,化成最简形式
	
	int r=n-pow(2,p);				//算出剩余的数
	if(r>0){
		cout<<"+";
		f(r);
	}
}
int main(){
	scanf("%d",&n);	
	f(n);							//头重脚轻的main函数
}

6.0 P2239 螺旋矩阵

P2239 螺旋矩阵
在这里插入图片描述
实现程序

#include<iostream>
#include<cstdio>
using namespace std;
int n,x,y;
int dfs(int cs,int sum,int h,int l)
{
    int tt=n-(cs-1)*2;
    if(tt<1) return 0;//退出循环的条件 
    
    if(h==1) return sum+l;
    if(l==tt) return sum+h+l-1;
    if(h==tt) return sum+(3*tt-l-1);
    if(l==1)return sum+(4*tt-h-2);		//边界:根据每层算出边缘上的值
    
    return dfs(cs+1,sum+4*tt-4,h-1,l-1);
} 
int main()
{
    scanf("%d%d%d",&n,&x,&y);
    int dd=dfs(1,0,x,y);
    cout<<dd<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值