第1章递归函数

文章详细介绍了递归函数设计中的数学归纳法,包括阶乘的递归实现,以及猴子吃桃、弹簧板问题和两种枚举算法(指数型和组合型)的应用。最后讨论了分形系统的坐标变换问题。

第1章 递归函数的设计技巧

  • 数学归纳法

  • 递归函数设计三个重要部分

  • 递归求阶乘

数学(结构)归纳法
  1. 验证P(1)成立
  2. 证明如果P(k)成立,那么P(k+1)成立
  3. 联合Step1和Step2,证明P(1)->P(n)成立
递归函数
  1. 给递归函数一个明确的语义
  2. 实现边界条件时的程序逻辑(p(1))
  3. 假设递归函数调用返回结果是正确的,实现本层函数逻辑 ( p(k) )
//递归 n的阶乘
//1.acm_1_1_diGui_test6代表n的阶乘的结果
int acm_1_1_diGui_test(int n){
    if(n==1)return 1; //边界条件 n==1
    return acm_1_1_diGui_test(n-1)*n; //利用f(n-1)结果计算f(n)的值
}

//猴子吃桃
//猴子吃n天桃子的数量
int acm_1_1_diGui_test2(int n){
    if(n==1)return 1;
    return (acm_1_1_diGui_test2(n-1)+1)*2;
}

例题

一个小球,掉落到一连串弹簧板上,每个弹簧板回弹a[i]个距离,问小球弹 几次会弹出弹簧板串

5

2 2 3 1 2

2表示会弹2个距离,所以是2+3==5 ,

到三会弹3个距离,5+3=8>5

所以弹了两次

  1. f(i)小球从i位置开始被弹出的次数
  2. i>=n 时结束
  3. f(i)=f(i+a[i])+1
i=0
res=0
n=int(input())
ns=list(map(int,input().split(" ")))
while(i<n):
    i+=ns[i]
    res+=1
print(res)

例题:

输出n的指数型枚举

in:

3

out:

1
1 2
1 2 3
1 3
2
2 3
3

分析:

f(i,j,n) ,i表示第i个位置,j表示最小,n表示最大

边界条件是j>n ,最小值比最大值大的时候

  1. 如何按照字典序输出?

从小到大进行枚举每个位置的数字就是字典序

  1. 如何保证每个位置数字都大于前面的数字?

传入一个量,标记当前位置的最小值

每个枚举的过程都传入另一个数字,这个数字他标记了当前位置最小可以选取的数字

  1. 如何输出?
int arr[10]; 
def fn(int i,int j,int n){
    if (j>n) return;
    for(int k=j;k<=n;k++){ //j代表最小值,只能输出从j到n
        arr[i]=k;//i代表第几个位置, 其中arr[j]一定比arr[i]前面的都大
        print_one_result(i); //输出前i个,i表示到了第几个位置
        fn(i+1,j+1,n); 
    }
}

//输出从arr数组从0到n的元素
void print_one_result(int n){
    cout << "n" << n << endl;
    for(int i=0;i<=n;i++){
        if(i){
            cout << " ";
        }
        cout << arr[i];
    }
    cout << endl;
}
int n;
cin >> n; //对n进行指数枚举
acm_1_1_diGui_fn3(0,1,n);
f(n)的n==ikarrji
01110
121 221
231 2 332
131 321
02210
132 331
03310

结论:

i==n为输出的个数

k为最后一个元素值

结束条件:j用来限定输出最小值,j永远不超过3

因为是递归,所以看k的值可以看出来属于哪一层递归

1

​ 2 3

​ 3

2

​ 3

3

arr=[0]*10
def print_one_result(n):
    for i in range(0,n+1):
        if i:
            print(" ",end="")
        print(arr[i],end="")
    print()


def fn3(i,j,n):
    if(j>n): return;
    for k in range(j,n+1):
        arr[i]=k
        print_one_result(i)
        fn3(i+1,k+1,n);

def fn3_Test():
    n=int(input())
    fn3(0,1,n)
fn3_Test()

例题:

递归实现组合型枚举

in:

3 2

out:

1 2

1 3

2 3

  1. 如何按照字典序输出所有方案?

枚举每个位置直接从小到大

  1. 当前位置可以选取的最小值是什么–设置变量

  2. 怎么输出?

if i ==m,就

f(i,j,n) 第i个位置的最小值j和最大值n

边界是是否有足够多数

void acm_1_1_print_one_result5(int n){
    for(int i=0;i<n;i++){
        if (i) cout << " ";
        cout << arr[i];
    }
    cout << endl;
}
void acm_1_1_diGui_fn5(int i,int j,int n,int m){
    if(i==m){
        acm_1_1_print_one_result5(m);
        return;
    }
    for(int k=j;k<=n && m-i-1<=n-k;k++){
        arr[i]=k;
        acm_1_1_diGui_fn5(i+1,k+1,n,m);
    }
    return;
}
void acm_1_1_diGui_test5(){
    int n,m; //n代表输入是几 ,m代表每次输出几个数
    cin >> n >> m;
    acm_1_1_diGui_fn5(0,1,n,m);
    //0 代表第几个位置
    //1 代表当前位置最小可以选择的值
    //n 代表当前位置最大可以选择的值
    //m 最多枚举多少倍
}

例题:

按照字典序列,输出所有1到n这n个整数的所有方案

  1. 递归函数长什么样?----f(i,n)
  2. i==n的时候-》返回输出
//按照字典序输出所有1到n这n个整数的方案
int arr6[10],vis6[10]={0};
void acm_1_1_print_one_result6(int n){
    for(int i=0;i<n;i++){
        if(i) cout << " ";
        cout << arr6[i];
    }
    cout << endl;
    return;
}
void acm_1_1_diGui_fn6(int i,int n){
    if(i==n){
        //开始输出
        acm_1_1_print_one_result6(n);
        return;
    }
    for(int k=1;k<=n;k++){
        if(vis6[k])continue; //k被使用过了
        arr6[i]=k;
        vis6[k]=1;
        acm_1_1_diGui_fn6(i+1,n);
        vis6[k]=0; //回收k
    }
}
void acm_1_1_diGui_test6(){
    int n; //n代表输入是几 ,m代表每次输出几个数
    cin >> n;
    acm_1_1_diGui_fn6(0,n);
    //0 代表第几个位置
    //1 代表当前位置最小可以选择的值
    //n 代表当前位置最大可以选择的值
    //m 最多枚举多少倍
}
acm_1_1_diGui_test6();

例题:

239不规则的街道

​ 分形系统:

同样的一个图形通过固定的变换到一个更大的图形,-继续更大的图形

//分形图形
void acm_1_1_diGui_fn7(long long n,long long s,long long &x,long long &y){
    //递归函数,求n级城市中,房子编号为s的房子坐标并将坐标存储在(x,y)变量中
    if(n==1){//当为1级城市的时候,直接返回
        if (s==1) x=0,y=0;
        else if(s==2) x=0,y=1;
        else if(s==3) x=1,y=1;
        else x=1,y=0;
        return;
    }
    long long L=1LL << (n-1);
    long long block=L*L; //每个区域点的数量
    long long xx,yy;
    //当前点在第几个区域中
    if (s <= block ) { //第一个区域,用坐标变换规则:(x,y)->(y,x)

        acm_1_1_diGui_fn7(n-1,s,xx,yy);
        x=yy,y=xx;
    }else if(s <= 2*block){ //第二个区域,(x,y)->(x,y+L)
        acm_1_1_diGui_fn7(n-1,s-block,xx,yy);
        x=xx,y=yy+L;
    }else if(s <= 3*block){ //第三个区域,(x,y)->(x+L,y+L)
        acm_1_1_diGui_fn7(n-1,s-2*block,xx,yy);
        x=xx+L,y=yy+L;
    }else{//第四个区域,(x,y)->(2L-y-1 ,L-x-1)
        acm_1_1_diGui_fn7(n-1,s-3*block,xx,yy);
        x=2*L-yy-1,y=L-xx-1;
    }
    return;

}
void acm_1_1_diGui_test7(){
    long long t,n,s,d;
    scanf("%lld",&t);
    while(t--){
        scanf("%lld%lld%lld",&n,&s,&d);
        long long sx,sy,dx,dy;
        acm_1_1_diGui_fn7(n,s,sx,sy);
        acm_1_1_diGui_fn7(n,d,dx,dy);
        printf("%.0lf\n",10*sqrt(S(sx-dx)+S(sy-dy)));
    }
}
void acm_1_1_diGui_test7(){
    long long t,n,s,d;
    scanf("%lld",&t);
    while(t--){
        scanf("%lld%lld%lld",&n,&s,&d);
        long long sx,sy,dx,dy;
        acm_1_1_diGui_fn7(n,s,sx,sy);
        acm_1_1_diGui_fn7(n,d,dx,dy);
        printf("%.0lf\n",10*sqrt(S(sx-dx)+S(sy-dy)));
    }
}
  • 完成2024.3.5
by cry
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

厨 神

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值