动态规划问题(一)

动态规划为决策过程最优化的数学方法

例1:爬楼梯

在爬楼梯的时候,每次可以向上走1阶台阶或者2阶台阶,问有n阶楼梯有多少种上楼的方式?

分析
由于每次最多爬2阶,楼梯的第i阶,只可能从楼梯第i-1阶与第i-2阶到达。故到达第i阶有多少种爬法,只与第i-1阶、第i-2阶的爬法数量直接相关。
即:第i阶的爬法数量=第i-1阶的爬法数量+第i-2阶的爬法数量
算法思路
设置递推数组dp[0…n],dp[i]代表到达第i阶,有多少走法。
dp[0]=0
dp[1]=1
dp[2]=2
dp[3]=dp[1]+dp[2]=3

dp[n]=dp[n-1]+dp[n-2]
在这里插入图片描述
代码如下

#include<iostream>
#include<vector>

    int  climbStairs(int n){
   std::vector<int>dp(n+3,0);
   dp[1]=1;
   dp[2]=2;
   for(int i=3;i<=n;i++){
   dp[i] = dp[i-1]+dp[i-2];
   }
   return dp[n];
}

int main(){
    int n;
    std::cin>>n;
    std::cout<<climbStairs(n)<<std::endl;
    return 0;
}

例2:打家劫舍

在一条直线上,有n个房屋,每个房屋中有数量不等的财宝,有一个盗贼希望从房屋中盗取财宝,由于房屋中有报警器,如果同时从相邻的两个房屋中盗取财宝就会触发报警器,问在不触发报警器的前提下,最多可以获取多少财宝。房间财宝数目为【5、2、6、3、1、7】

分析:
a,若选择第i个房间盗取财宝,就不能选第i-1个房间
b,若不选择第i个房间盗取财宝,则相当于只考虑前i-1个房间盗取。
确认原问题和子问题
原问题为求n个房间的最优解,子问题为求前1个房间、前2个房间的最优解。
确认状态:
第i个状态即为前i个房间能获得的最大财宝。
确认边界状态
前一个房间的最优解,第一个房间的财宝
前两个房间的最优解,前两个房间的较大财宝值
确定状态转移方程
a,选择第i个房间:第i个房间+前i-2个房间的最优解
b,不选择第i个房间:前i-1个房间的最优解
动态规划转移方程:
dp[i]=max(dp[i-1],dp[i-2]+nums[i]);(i>=3)

[5、2、6、3、1、7]
设第i个房间的最优解为dp[i]
dp[1]=5;
dp[2]=5;
dp[3]=max(dp[2],dp[1]+nums[3])=max(5+6,5)=11


代码如下:

#include<iostream>
#include<vector>
class Solution{
	public:
	int rob(std::vector<int>& nums){

    if(nums.size() ==0){}
return 0;
}
    if(nums.size() == 1){
	return nums[0];
}

std::vector <int>dp(nums.size(),0);  //设置第i个房间的最优解为dp[i]
dp[0]=nums[0];
dp[1]=std::max(nums[0],nums[1]);
for(int i=2;i <nums.size();i++){
	dp[i] = std::max(dp[i-1],dp[i-2]+nums[i]);
	
	
	
} 
return  dp[nums.size()-1];
}
	
};

   int main(){
   	Solution solve;
    std::vector<int>nums;
    nums.push_back(5);
    nums.push_back(2);
    nums.push_back(6);
    nums.push_back(3);
    nums.push_back(1);
    nums.push_back(7);
    std::cout<<solve.rob(nums)<<std::endl;
    return 0;
}

例3:最大子段和

给定一个数组,求这个数组的连续子数组,最大的那一段的和。

分析:
实际上第i个状态代表前i个数字组成的连续的最大子段和,并不能根据dp[i-1]、dp[i-2]…推导出。
例如:
[-2、1、1、-3、4]
dp[0]=-2
dp[1]=1
dp[2]=2
dp[3]=2
dp[4]=[-2、1、1、-3]=2
dp[5]=[-2、1、1、-3、4]=4
两者不相邻,无法构成连续的子数组,之间没有内在联系,故无法进行推导。
则可以这样考虑动态规划算法:
第i个状态dp[i]即为以第i个数字结尾的最大子段和(最优解)。由于以第i-1个数字结尾的最大子段和dp[i-1]和nums[i]相邻:
dp[0]=nums[0]
dp[1]=max(dp[0]+nums[1],nums[1])

代码如下:

#include<iostream>
#include<vector>

int  climbStairs(std::vector<int>& nums){
   std::vector<int>dp(9,0);
   dp[0]=nums[0];
   int res= nums[0];
   for(int i=1;i<=8;i++){
   dp[i] = std::max(dp[i-1]+nums[i],nums[i]);
   
   if(res < dp[i]){
   	res = dp[i];
   }
   }
   return res;
}

int main(){
   
   std::vector<int>nums;
nums.push_back(-2);
nums.push_back(1);
nums.push_back(-3);
nums.push_back(4);
nums.push_back(-1);
nums.push_back(2);
nums.push_back(1);
nums.push_back(-5);
nums.push_back(4);
    std::cout<<climbStairs(nums)<<std::endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值