初级算法-动态规划

文章目录

        • 爬楼梯
          • 题意:
          • 解:
          • 代码:
        • 买卖股票的最佳时机
          • 题意:
          • 解:
          • 代码:
        • 最大子序和
          • 题意:
          • 解:
          • 代码:
        • 打家劫舍
          • 题意:
          • 解:
          • 代码:

爬楼梯

题意:

从0开始,每次+1或者+2,求和为n有多少种可能

解:

DP[i]表示到达i能有几种可能

初始状态DP[0]=1

递推公式DP[i]=DP[i-1]+DP[i-2]

因为每次只能+1或+2,所以而状态i-1一定不会影响(改变)i-2所以正向遍历即可(i-2会改变i-1

代码:
#include
#include
using namespace std;
constexpr int N=50;
int climbStairs(int n)
{
    int dp[N];
    memset(dp,0,sizeof dp);
    dp[0]=1;
    for(int i=0;i>ans;
    ans=climbStairs(ans);
    cout<

买卖股票的最佳时机

题意:

预知股票价格,求一次买入+一次卖出能赚的最大金额

解:

DP[i]表示第i天前的最低价格

对于第Day天的价格我们要找Day之前最便宜的股票买,所以递推式DP[day]=min{ Day[0,Day) }

而初始状态第一天之前的价格我们设置成>=prices[0]即可,因为这时候是不赚钱甚至亏钱的,保持答案0

那么对于每一天day我们先用今天的价格prices[day]-DP[day-1]获取今天的最高利润,然后DP[day]=min(prices[day],DP[day-1])更新这一天之前(包含)的最低价格

由于是一次买入一次卖出,所以取最高值,并且DP[i]只和DP[i-1]有关,而这两者都是基于prices[i]计算/更新,

所以用一个变量aftre充当DP[i-1]即可

代码:
#include
using namespace std;
int maxProfit(vector& prices)
{
    int lg=prices.size();
    int after=INT_MAX,ans=0;
    for(auto &price:prices)
    {
        ans=max(ans,price-after);
        after=min(after,price);
    }
    return ans;
}
int main()
{
    vector prices;int temp;
    while(cin>>temp)
    {
        prices.push_back(temp); 
    }
    int ans=maxProfit(prices);
    cout<

最大子序和

题意:

如题。子数组 是数组中的一个连续部分。

解:

主要说一下分治法

思路就是一个区间[l,r]内的最大子数组要么处于[l,mid]要么处于[mid+1,r],或者处于[l,r],虽然很像废话,但是对计算机来说不一样,由于要找子数组,所以要保持连续性,所以当这个区间内的最大子数组在[l,r]情况下,一定是包含[l,mid]的右边和[mid+1,r]的左边

那么对于一个区间,我们维护它的左起最大子数组、右起最大子数组还有无条件子数组以及区间和

分别有什么作用呢?对于最小子区间[t,t]这四个数值都是nums[t]

而对于一个父区间,它的左起子数组 是max [l,mid]->left ,[mid+1,r]->left+[l,mid]->sum,为了保持连续性,只有两种可能,要么是左子区间的 左起最大子数组,要么是整个左子区间加上右子区间的 左起最大子数组

对于右起最大子数组也是一样的,保留右起的连续性;

还有一种无条件子数组,指的是不一定包含左起点或者右起点的最大子数组,由于这可能是答案的一部分,所以要对它进行保留和计算,计算方式是<左子区间的右连上右子区间的左>和两个子区间的无条件子数组中取最大值

代码:
#include
using namespace std;
struct ITEM
{
    int l,r,m,sum;  
};
ITEM query(int l,int r,vector& nums)
{
    //cout<<"l:"<>1;
    ITEM item1=query(l,mid,nums);
    ITEM item2=query(mid+1,r,nums);
    ITEM ret;
    ret.l=max(item1.l,item1.sum+item2.l);
    ret.r=max(item2.r,item2.sum+item1.r);
    ret.m=max(item2.l+item1.r,max(item1.m,item2.m));
    ret.sum=item1.sum+item2.sum;
    return ret;  
}
int maxSubArray(vector& nums)//分治法 
{
    int lg=nums.size();
    ITEM ret=query(0,lg-1,nums);
    int ans=max(ret.l,max(ret.r,ret.m));
    return ans;
}
/*
int maxSubArray(vector& nums)//遍历法O(n) 
{
    int lg=nums.size(),temp=0,ans=nums[0];
    for(auto &num:nums)
    {
        temp+=num;
        ans=max(ans,temp);
        if(temp<0) temp=0;
    }
    return ans;
}*/
int main()
{
    vector nums;int num;
    while(cin>>num) 
        nums.push_back(num);
    int ans=maxSubArray(nums);
    cout<

打家劫舍

题意:

一个非负整数数组,从中选择数字字求最大和,要求不能选择连续的数字

解:

先将问题想的复杂一点,DP[0][i]表示第i个数字不选择时区间[0,i]的最大和,DP[1][i]表示选择第i个数字,这样可轻松推出dp[0][i]=max(dp[0][i-1],dp[1][i-1])dp[1][i]=max(nums[i]+dp[0][i-1],dp[0][i-1])

但是我们再想一想,DP[i]表示第i个数字(不管选不选)时区间[0,i]的最大和,那么由于不知道i是否被选上,所以我们的递推式只能变成DP[i]=max(DP[i-1],DP[i-2]+nums[i]),这样是否合理呢?

是合理的因为DP[i]不管选没选择这个递推式都不会选择连续的数字,只要初始状态合理就行,初始状态设置DP[0]=nums[0] DP[1]=max(nums[0],nums[1]),从第一步DP[2]的计算我们就可以看出,无论如何我们都不会计算DP[2]包含nums[1]+nums[2],同时由于这个递推式只需要三个变量,所以可以用滚动变量解决

代码:
#include
using namespace std;
int rob(vector& nums)//J2
{
    int lg=nums.size();
    if(lg==1) return nums[0];
    int temp1=nums[0],temp2=max(nums[0],nums[1]);
    for(int i=2;i& nums)//J1
{
    int lg=nums.size();
    vector>dp(2,vector(lg));
    dp[1][0]=nums[0];
    for(int i=1;i nums;int num;
    while(cin>>num) 
        nums.push_back(num);
    int ans=rob(nums);
    cout<

你可能感兴趣的:(力扣每日一题,算法,动态规划,leetcode,c++)