2019.9.28
!!!
本蒟蒻的第一篇上课笔记
今天又听了一遍递归
本课堂笔记目录
-
递归函数的定义与使用
-
汉诺塔问题
-
放苹果问题(可以为空)(luogu-P2386 放苹果)
-
放苹果问题(不可以为空)(luogu-T98856 集合的划分)
-
数的划分(P1025 数的划分)
-
幂次方(P1010 幂次方)
-
P2239 螺旋矩阵(P2239 螺旋矩阵)
0.0 递归函数的定义与使用
1.0 汉诺塔问题
这个游戏很有意思,没有玩过的小伙伴请移步7k7k或4399后看一下
(划水哈哈哈哈 goto---->7k7k新汉诺塔 )
本蒟蒻只能用代码过关了qwq
本题思路:
- 如果有0个盘子,程序结束
- 第一步先把n-1个盘子借助c柱子移到b柱子上
- 第二步就把a柱上剩下的的那个盘子直接移到c柱上
- 把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个盘子里面
这个我们分两种情况
!
- 当前位置不放苹果那么方案数为
f(m,n-1)
相当于把m个苹果压榨到了n-1个盘子里(空一个盘子给蔡徐坤) - 第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那么方案数为
f(n-1,k-1)
相当于最后1个放1; n-1全部挤到前面的盘子里!
(糟糕的特权!) - 第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 幂次方
方法一:用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;
}