动态规划的关键点:一系列以空间换时间的算法
tips1:有n级台阶,一个人每次上一级或者两级,问有多少种走完n级台阶的方法。
解析:动态规划的实现的关键在于能不能准确合理的用动态规划表来抽象出 实际问题。在这个问题上,我们让f(n)表示走上n级台阶的方法数。那么当n为1时,f(n) = 1,n为2时,f(n) =2,就是说当台阶只有一级的时候,方法数是一种,台阶有两级的时候,方法数为2。那么当我们要走上n级台阶,必然是从n-1级台阶迈一步或者是从n-2级台阶迈两步,所以到达n级台阶的方法数必然是到达n-1级台阶的方法数加上到达n-2级台阶的方法数之和。即f(n) = f(n-1)+f(n-2),我们用dp[n]来表示动态规划表,dp[i],i>0,i<=n,表示到达i级台阶的方法数。
到达n级台阶的方法数必然是到达n-1级台阶的方法数加上到达n-2级台阶的方法数之和。即f(n) = f(n-1)+f(n-2)
/*dp是全局数组,大小为n,全部初始化为0,是题目中的动态规划表*/
int fun(int n){
if (n==1||n==2)
return n;
/*判断n-1的状态有没有被计算过*/
if (!dp[n-1])
dp[n-1] = fun(n-1);
if(!dp[n-2])
dp[n-2]=fun(n-2);
return dp[n-1]+dp[n-2];
}
tips2:给定数组arr,返回arr的最长递增子序列的长度,比如arr=[2,1,5,3,6,4,8,9,7],最长递增子序列为[1,3,4,8,9]返回其长度为5.
分析:首先生成dp[n]的数组,dp[i]表示以必须arr[i]这个数结束的情况下产生的最大递增子序列的长度。对于第一个数来说,很明显dp[0]为1,当我们计算dp[i]的时候,我们去考察i位置之前的所有位置,找到i位置之前的最大的dp值,记为dpj,dp[j]代表以arr[j]结尾的最长递增序列,而dp[j]又是之前计算过的最大的那个值,我们在来判断arr[i]是否大于arr[j],如果大于dp[i]=dp[j]+1.计算完dp之后,我们找出dp中的最大值,即为这个串的最长递增序列。
每次max=dp[j]都指的是前一次的最大值 例如 i=1 j只能等于0 此时max=0 dp[j]=1(如果没有if(dp[j]>_max&&arr[i]>arr[j]),即后面的数大于前面的数 就没有max=dp[j] )那么即使 dp[i]=max+1 也仍然使最长子序列为1
每次进入循环都会清零max 当i=3时 可以取的值有 0 1 2 只要arr[i]>arr[j] 最长子序列势必会增一个
每次dp[j]>_max&&arr[i]>arr[j]不符合条件就会使max仍然继承上次的值 max=0 第一次成立 max=1;dp[i]=2
第二次如果不成立,max=1仍然 此时dp仍然等于2 (0 2 1类似于这种
#include <iostream>
#include <algorithm>
using namespace std;
/*动态规划表*/
int dp[5] = {};
int main(){
int arr[5] = {2,4,5,3,1};
dp[0] = 1;
1. List item
const int oo = 0;
for (int i = 1;i<5;i++){
int _max = oo;
for (int j=0;j<i;j++)
if(dp[j]>_max&&arr[i]>arr[j])
_max = dp[j];
dp[i] = _max+1;
}
int maxlist=0;
for (int i = 0; i < 5;i++)
if (dp[i] > maxlist)
maxlist=dp[i];
cout << maxlist << endl;
}
、
tips3:背包问题,动态规划经典问题,一个背包有额定的承重W,有N件物品,每件物品都有自己的价值,记录在数组V中,也都有自己的重量,记录在数组W中,每件物品只能选择要装入还是不装入背包,要求在不超过背包承重的前提下,选出的物品总价值最大。
分析:分析:假设物品编号从1到n,一件一件的考虑是否加入背包,假设dp[x][y]表示前x件物品,不超过重量y的时候的最大价值,枚举一下第x件物品的情况:
情况1:如果选择了第x件物品,则前x-1件物品得到的重量不能超过y-w[x]。
情况2:如果不选择第x件物品,则前x-1件物品得到的重量不超过y。
所以dp[x][y]可能等于dp[x-1][y],也就是不取第x件物品的时候,价值和之前一样,也可能是dp[x-1][y-w[x]]+v[x],也就是拿第x件物品的时候,当然会获得第x件物品的价值。两种可能的选择中,应该选择价值较大的那个,也就是:
dp[x][y] = max{dp[x-1][y],dp[x-1][y-w[x]]+v[x]}
m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]);当碰到一个需要选择的时候有两种可能 1.即将拿的这个物品的重量大于我的背包重量,选择不拿2.即将拿的物品小于我的背包重量,此时考虑拿这件物品和不拿这件物品哪个价值更大((1)不拿 (2)拿同时考虑 当前重量的价值 加上背包重量减去重量的价值
#include <iostream>
#include <cstring>
using namespace std;
const int N=15;
int main()
{
int v[N]={0,8,10,6,3,7,2};
int w[N]={0,4,6,2,2,5,1};
int m[N][N];
int n=6,c=12;
memset(m,0,sizeof(m));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=c;j++)
{
if(j>=w[i])
m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]);
else
m[i][j]=m[i-1][j];
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=c;j++)
{
cout<<m[i][j]<<' ';
}
cout<<endl;
}
return 0;
}
tips4:
给定一个矩阵m,从左上角开始每次只能向右走或者向下走,最后达到右下角的位置,路径中所有数字累加起来就是路径和,返回所有路径的最小路径和,如果给定的m如下,那么路径1,3,1,0,6,1,0就是最小路径和,返回12.
1 3 5 9
8 1 3 4
5 0 6 1
8 8 4 0
分析:分析到达某个点只需判断它是是左过来还是从右过来更优就可以了
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int value[1024][1024];
int main()
{
int m, n;
scanf_s("%d%d", &m, &n);
//输入矩阵
for (int i = 0; i < m; ++i)
{
for (int j = 0; j < n; ++j)
{
scanf_s("%d", &value[i][j]);
}
}
//计算
for (int i = 1; i < m; ++i)
{
value[i][0] += value[i - 1][0];
}
for (int j = 1; j < n; ++j)
{
value[0][j] += value[0][j - 1];
}
for (int i = 1; i < m; ++i)
{
for (int j = 1; j < n; ++j)
{
value[i][j] += max(value[i - 1][j], value[i][j - 1]);
}
}
打印调试
//for (int i = 0; i < m; ++i)
//{
// for (int j = 0; j < n; ++j)
// {
// cout << value[i][j] << " ";
// }
// cout << endl;
//}
//cout << endl;
cout << value[m - 1][n - 1] << endl;
return 0;
}