DP的概念
dp(即动态规划)用于解决具有重叠子问题和最优子结构问题。下面以斐波那契数列为例说明DP的概念。斐波那契数列是一个递推数列,每个数是前两个数之和,如1,1,2,3,5,8,13......,计算第n个斐波那契数,递推公式为,用递归编程求斐波那契数列,代码如下。
int fib(int n){
if(n==1||n==2) return 1;
return fib(n-1)+fib(n-2);
}
这段代码以2倍增,复杂度,太慢了,用动归可以优化。
DP的两种编程方法
1.记忆化
原本的递归树长这样
但是呢,这里面有很多多余的运算,把这些多余的去掉,就可以优化了。比如说,节点3就算了两次,节点2算了三次,我们只需要把节点3的值用数组记住,到了第二个3时就不用再算了。这就叫记忆化。代码如下,
int a[10000];
int fib(int n){
if(n==1||n==2) return 1;
if(a[n]!=0) return a[n];
return a[n]=fib(n-1)+fib(n-2);
}
这一次的复杂度为 ,优化成功。但是请你看这个东西。
#include <bits/stdc++.h>
using namespace std;
long long a[100000000];
long long fib(long long n){
if(n==1||n==2) return 1;
if(a[n]!=0) return a[n];
return a[n]=fib(n-1)+fib(n-2);
}
int main(){
long long n;cin>>n;
cout<<fib(n);
return 0;
}
输入65536时,虽然没有超过long long范围,但是请你看下图
这个问题属于栈内存溢出,即便是记忆化也逃不掉这个“噩梦”
解决方法:最最最简单,把变为,这样直接把栈的最大内存变为n/2
2. DP
思路:定义一个数组dp[],代表fib(n)的值,可以列出如下表格
dp[1] | 1 |
dp[2] | 1 |
dp[3] | 2 |
dp[4] | 3 |
dp[5] | 5 |
dp[6] | 8 |
dp[7] | 13 |
...... | ...... |
代码如下
const int N=255;
int dp[N];
int fib(int n){
dp[1]=dp[2]=1;
for(int i=3;i<=n;i++) dp[i]=dp[i-1]+dp[i-2];
return dp[n];
}
一圈三连,必有回关。你的关注,就是我创作的动力。(~ ̄▽ ̄)~