P5739 计算阶乘 题解

本文同步发表在洛谷专栏

我们充分发扬人类智慧:
将递推和递归混为一谈
在 dp 的基础上来学递归
然后把递推和 dp 混为一谈
然后我就发现:
™的我 dp 没学好!
然后去学 dp,
然后发现我递推没学好,
所以四舍五入我递归也学不好,
那就不学了!
image

好了让我们步入正题。


正文

首先,递归recursive需要一个函数,我们叫这个函数rec

int rec(int a){

}
void rec(int a){

}

然后我们开始解题:

P5739 计算阶乘

题目描述

\(n!\),也就是 \(1\times2\times3\dots\times n\)
挑战:尝试不使用循环语句(for、while)完成这个任务。

解题过程

暴力

在我们啥也不知道的时候,肯定是使用for循环暴力出奇迹:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n,ans=1;
    cin>>n;
    for(int i=1;i<=n;i++){
        ans*=i;
    }
    cout<<ans;
}
-----------------------
编程语言:C++20 O2

代码长度:160B

用时:16ms

内存:680KB

AC 记录

动态规划,dp

首先,明白题目让我们做什么,显然是算出 \(1 \times 2 \times 3 \times ...... \times n-1 \times n\)

接着,确认第 \(i-1\) 步的状态是 \(1 \times 2 \times ...... \times i-1\),第 \(i\) 步的状态是 \(1 \times 2 \times ...... \times i-1 \times i\),因此,我们得出 dp 公式:

\[dp[i]=dp[i-1] \times i \]

然后,我们可以写出以下代码:

#include<bits/stdc++.h>
using namespace std;
map<int,int>dp;
int main(){
    int n;
    cin>>n;
    dp[1]=1;
    for(int i=2;i<=n;i++){
        dp[i]=dp[i-1]*i;
    }
    cout<<dp[n];
}
-------------------------------
编程语言:C++20 O2

代码长度:196B

用时:20ms

内存:564KB

怎么比暴力还慢?

AC 记录

递归

这就是题面中说的挑战部分,以前我还不会做,现在会了。

根据上文我们提到的 dp 公式:

点击查看 dp 公式

\[dp[i]=dp[i-1] \times i \]

通过 \(dp\) 公式我们不难看出这就是用上一次运算的得数 \(\times i\),明确了这个后,有点基础的人就不难写出递归函数了,但是我假定你没有基础但是我还是要引用别人的文章引经据典地告诉你递归是个神马东东:

我们先来看一下递归(recursion)的定义:

递归是一种解决问题的有效方法,在递归过程中,函数将自身作为子例程调用。

简单说程序调用自身的编程技巧叫递归。递归的思想是把一个大型复杂问题层层转化为一个与原问题规模更小的问题,问题被拆解成子问题后,递归调用继续进行,直到子问题无需进一步递归就可以解决的地步为止。
使用递归需要避免出现死循环,为了确保递归正确工作,递归程序应该包含2个属性:
基本情况(bottom cases),基本情况用于保证程序调用及时返回,不在继续递归,保证了程序可终止。
递推关系(recurrentce relation),可将所有其他情况拆分到基本案例。

转载自https://zhuanlan.zhihu.com/p/150562212

好了,大概明白递归是什么后,考虑每一步(或者说是调用函数本身一次)要做什么?显然是 \(\times i\)。那如何实现呢?

其实底层逻辑不难理解,就像 \(dalao\) image 说的那样,这完全可以比作拆快递,这个代码会一直把快递拆下去,直到 \(i=1\),它才会开始合上快递,如下图:

image

这里黄色笔就是返回的过程及其返回值,也就是拿出商品把快递箱合上。

递归函数长这样:

int rec(int i){          //1
    if(i==1){            //2
        return 1;        //3
    }                    //4
    return i*jc(i-1);    //5
}                        //6
代码数据:

编程语言:C++20

代码长度:204B

用时:15ms

内存:576KB

AC 记录

好的,接下来是我看到题解区的题解后才写进来的方法,不建议使食用。

打表

首先,点击这个:image

搜索计算器:

image

打开计算器,然后计算出 \(1!\)~\(12!\) 的结果(\(n!=1 \times 2 \times 3 \times ...... \times n-1 \times n\))。这里帮大家算好了:

  • \(1!=1\)

  • \(2!=2\)

  • \(3!=6\)

  • \(4!=24\)

  • \(5!=120\)

  • \(6!=720\)

  • \(7!=5040\)

  • \(8!=40320\)

  • \(9!=362880\)

  • \(10!=3628800\)

  • \(11!=39916800\)

  • \(12!=479001600\)

然后我们就可以愉快地开始打表啦!

#include<bits/stdc++.h>
using namespace std;
int ans[13]={0,1,2,6,24,120,720,5040,40320,362880,3628800,39916800,479001600};
int main(){
    int n;
    cin>>n;
    cout<<ans[n];
    return 0;
}
---------------------
代码数据:

编程语言:C++20

代码长度:200B

用时:15ms

内存:564KB

AC 记录

posted @ 2024-02-20 19:47  StarsTwinkle  阅读(85)  评论(1)    收藏  举报