动态规划(Dynamic Progamming,DP)是一种用来解决一类最优化问题的算法思想
简单来说
动态规划将一个复杂的问题分解成若干个子问题
通过子问题的最优解来得到原问题的最优解
tips:动态规划如何记录子问题的解
来避免下次遇到相同的子问题时的重复计算
以斐波那契数列代码为例
有很多重复计算的过程
为避免重复计算
开一个一维数组dp
用以保存已经计算过的结果
其中dp[n]记录F(n)的结果
并用dp[n]=-1表示F(n)当前还没有被计算过
int dp[maxn];
int F(int n)
{
if(n==0||n==1)
return 1;//递归边界
if(dp[n]!=-1)
return dp[n];//已经计算过,直接返回结果,不用重复计算
else
{
dp[n]=F(n-1)+F(n-2);//计算F(n),并保存至dp(n)
return dp[n];
}
}
经典数塔问题:
dp[i][j]表示从第i行第j个数字出发的到达最底层的所有路径中的最大值
【分析】
如果要求出dp[i][j],一定要先求出它的两个子问题
从位置dp(i+1,j)到达最底层的最大和dp[i+i][j]和
从位置dp(i+1,j+1)到达最底层的最大和dp(i+1,j+1)
进行了一次决策
走位置(i,j)的左下还是右下
所以
dp[i][j]=max(dp[i+1][j],dp[i][j])+f[i][j]
这就是状态转移方程
但是可以发现
数塔的最后一层的dp值总是等于数组本身
即dp[n][j]=f[n][j](1<=j<=n)
这种就是边界
而动态规划的地推写法总是从这些边界出发
通过状态转移方程扩散到整个dp数组
这样就可以从最底层各位置的dp值出发
不断往上求出每一层各位置的dp值
最后就会得到dp[1][1]
即答案
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
#define maxn 1000
int f[maxn][maxn],dp[maxn][maxn];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
scanf("%d",&f[i][j]);//输入数塔
}
}
//边界
for(int j=1;j<=n;j++)
{
dp[n][j]=f[n][j];
}
for(int i=n-1;i>=1;i--)
{
for(int j=1;j<=i;j++)
{
dp[i][j]=max(dp[i+1][j],dp[i+1][j+1])+f[i][j];
}
}
printf("%d\n",dp[1][1]);
return 0;
}
递推写法
自下而上