- 将一个复杂的问题分解成若干个子问题,通过综合子问题的最优解来得到原问题的最优解
- 动态规划会将每个求解过的子问题的解记录下来,这样下一次碰到同样的子问题时,就可以直接使用之前记录的结果,而不是重复计算
- 可以用递归或者递推的写法实现,递归的写法又叫记忆化搜索
- 重叠子问题:如果一个问题可以被分解成若干个子问题,且这些子问题会重复出现,就称这个问题拥有重叠子问题。 一个问题必须拥有重叠子问题,才能用动态规划去解决。
- 最优子结构:如果一个问题的最优解可以由其子问题的最优解有效地构造出来,那么称为这个问题拥有的最有子结构。最优子结构保证了动态规划中的原问题的最优解可以由子问题的最优解推导而来
- 动态规划与分治的区别:都是分解为子问题然后合并子问题得到解,但是分治分解出的子问题是不重叠的
- 动态规划与贪心的区别:都有最优子结构,但是贪心直接选择一个子问题去求解,会抛弃一些子问题,这种选择的正确性需要用归纳法证明,而动态规划会考虑所有的子问题
点击打开链接(翔哥博客)
//递归写法
int f(int n)
{
if(n==0) return1;
if(n==1) return 1;
else
{
return f(n-2)+f(n-1);
}
}
//记忆化递归->动态规划
int dp[1000];
memset(dp,-1,sizeof(dp));
dp[0]=1;
dp[1]=1;
int f(int n)
{
if(dp[n]!=1) return dp[n];
dp[n]=f(n-1)+f(n-2);
return dp[n];
}
数字三角形(POJ1163)
在上面的数字三角形中寻找一条从顶部到底边的路径,使得路径上所经过的数字之和最大。路径上的每一步都只能往左下或 右下走。只需要求出这个最大和即可,不必给出具体路径。 三角形的行数大于1小于等于100,数字为 0 - 99
输入格式:
5 //表示三角形的行数 接下来输入三角形
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
#include<iostream>
using namespace std;
int dp[100];
int f[100][100];
/*
数塔
首先初始化 dp[n][j]=f[n][j]//(1~n)
dp[i][j]=f[i][j]+max(dp[i+1][j],dp[i+1][j+1]);//(n-1~1)从下向上递推
//优化区间
dp[100]
dp[j]=f[n][j]//(1~n)
dp[i]=f[i][j]+max(dp[j],dp[j+1]);
*/
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
cin>>f[i][j];
}
}
// for(int i=1;i<=n;i++)
// dp[n][i]=f[n][i];
// for(int i=n-1;i>=1;i--)
// {
// for(int j=1;j<=i;j++)
// {
// dp[i][j]=f[i][j]+max(dp[i+1][j],dp[i+1][j+1]);
// }
// }
// for(int i=1;i<=n;i++)
// {
// for(int j=1;j<=i;j++)
// {
// cout<<dp[i][j]<<" ";
// }
// cout<<endl;
// }
for(int i=1;i<=n;i++)
dp[i]=f[n][i];
for(int i=n-1;i>=1;i--)
{
for(int j=1;j<=i;j++)
{
dp[j]=f[i][j]+max(dp[j],dp[j+1]);
}
}
cout<<dp[1]<<endl;
return 0;
}