动态规划(dp)(二)

按摩师

 按摩师

1.状态表示

dp【i】表示:到i位置时,此时最长时长

继续细化:在i位置选预约,或不选预约

f【i】:到i位置时,nums【i】必选的,最长时长

g【i】:到i位置时,nums【i】不选的,最长时长

2状态转移方程

f【i】=g【i-1】+nums【i】

g【i】=max(f【i-1】,g【i-1】)

3初始化

f【0】=nums【0】

g【0】=0

4.填表顺序

左往右

5.返回值

max(f【n-1】,g【n-1】)

class Solution {
public:
    int massage(vector<int>& nums) {
        int n=nums.size();
        if(n==0)
            return 0;
        vector<int>f(n);
        vector<int>g(n);
        f[0]=nums[0];
        for(int i=1;i<n;i++)
        {
            f[i]=g[i-1]+nums[i];
            g[i]=max(f[i-1],g[i-1]);
        }
        return max(f[n-1],g[n-1]);

    }
};

打家劫舍

198. 打家劫舍

 1.dp表示:

f【i】:表示到i位置时,必偷i位置房屋,获得的最大金额

g【i】:表示到i位置时,不偷i位置房屋,获得的最大金额

f【i】=g【i-1】+nums【i】

g【i】=max(f【i-1】,g【i-1】)

f【0】=nums【0】

返回值

max(f【n-1】,g【n-1】)

class Solution {
public:
    int rob(vector<int>& nums) {
        int n =nums.size();
        vector<int>f(n);
        vector<int>g(n);
        f[0]=nums[0];

        for(int i=1;i<n;i++)
        {
            f[i]=g[i-1]+nums[i];
            g[i]=max(f[i-1],g[i-1]);
        }
        return max(f[n-1],g[n-1]);
    }
};

打家劫舍 II

213. 打家劫舍 II

环形问题转化

分情况偷0下标的和不偷0下标的

当偷【0】时,转化为打家劫舍一:从【2,n-2】的打家劫舍

不偷【0】,转化为从【1,n-1】的打家劫舍

class Solution {
public:
    int rob(vector<int>& nums) {
        int n=nums.size();
        if(n==1)
            return nums[0];
        else if(n==2)
            return max(nums[0],nums[1]);
        else if(n==3)
            return max(max(nums[0],nums[1]),nums[2]);
        int a=nums[0]+_rob(nums,2,n-2);
        int b=_rob(nums,1,n-1);
        return max(a,b);
    }
    int _rob(vector<int>&nums,int left,int right)
    {
        int n=nums.size();
        vector<int>f(n);
        vector<int>g(n);

        f[left]=nums[left];
        for(int i=left+1;i<=right;i++)
        {
            f[i]=g[i-1]+nums[i];
            g[i]=max(g[i-1],f[i-1]);
        }
        return max(f[right],g[right]);
    }
};

删除并获得点数

740. 删除并获得点数

 转换为打家劫舍问题

用一个arr数组:arr【i】表示i出现的总和


class Solution {
public:
    const int N=10001;
    int deleteAndEarn(vector<int>& nums) 
    {
        int n=nums.size();
        vector<int>arr(N);
        for(int v:nums)
        {
            arr[v]+=v;
        }
        vector<int>f(N);
        vector<int>g(N);

        for(int i=1;i<N;i++)
        {
            f[i]=g[i-1]+arr[i];
            g[i]=max(g[i-1],f[i-1]);
        }
        return max(f[N-1],g[N-1]);
    }
};

粉刷房子

LCR 091. 粉刷房子

dp【i】位置分三种状态,

分别时刷红,蓝,绿

用dp【i】【0,1,2】分别表示到第i位置时,刷红蓝绿的成本

class Solution {
public:
    int minCost(vector<vector<int>>& costs) {
        int n=costs.size();
        vector<vector<int>>dp(n,vector<int>(3));
        dp[0][0]=costs[0][0];
        dp[0][1]=costs[0][1];
        dp[0][2]=costs[0][2];
        for(int i=1;i<n;i++)
        {
            dp[i][0]=min(dp[i-1][1],dp[i-1][2])+costs[i][0];
            dp[i][1]=min(dp[i-1][0],dp[i-1][2])+costs[i][1];
            dp[i][2]=min(dp[i-1][1],dp[i-1][0])+costs[i][2];
        }
        return min(dp[n-1][2],min(dp[n-1][0],dp[n-1][1]));
    }
};

 309. 买卖股票的最佳时机含冷冻期

买卖股票的最佳时机含冷冻期

309. 买卖股票的最佳时机含冷冻期

状态转移:

状态机:可买(可交易)dp【0】,可卖(买入)dp【1】,冷冻期dp【2】

dp【i】【0】=max(dp【i-1】【0】,dp【i-1】【2】)

dp【i】【1】=max(dp【i-1】【0】-costs【i】,dp【i-1】【1】)

dp【i】【2】=dp【i-1】【1】+costs【i】

初始化

dp【0】【0】=0

dp【0】【1】=-cost【0】

dp【0】【2】=0

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n=prices.size();
        vector<vector<int>>dp(n,vector<int>(3));
        dp[0][1]=-prices[0];
        for(int i=1;i<n;i++)
        {
            dp[i][0]=max(dp[i-1][0],dp[i-1][2]);
            dp[i][1]=max(dp[i-1][0]-prices[i],dp[i-1][1]);
            dp[i][2]=dp[i-1][1]+prices[i];
        }
        return max(dp[n-1][0],dp[n-1][2]);
    }
};

买卖股票的最佳时机含手续费

714. 买卖股票的最佳时机含手续费

dp【i】【0】可买

dp【i】【1】可卖

dp【i】【0】=max(dp【i-1】【0】,dp【i-1】【1】+prices【i】-fee)

dp【i】【1】=max(dp【i-1】【1】,dp【i-1】【0】-prices【i】)

dp【0】【0】=0

dp【0】【1】=-prices【0】

class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) 
    {
        int n=prices.size();
        vector<vector<int>>dp(n,vector<int>(3));
        dp[0][1]=-prices[0];
        for(int i=1;i<n;i++)
        {
            dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]-fee);
            dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i]);
        }
        return dp[n-1][0];
    }
};

123. 买卖股票的最佳时机 III

188. 买卖股票的最佳时机 IV

122. 买卖股票的最佳时机 II

122. 买卖股票的最佳时机 II

dp【i】【0】可买

dp【i】【1】可卖

dp【i】【0】=max(dp【i-1】【0】,dp【i-1】【1】+prices【i】)

dp【i】【1】=max(dp【i-1】【1】,dp【i-1】【0】-prices【i】)

dp【0】【0】=0

dp【0】【1】=-prices【0】

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n=prices.size();
        vector<vector<int>>dp(n,vector<int>(2));
        dp[0][1]=-prices[0];
        for(int i=1;i<n;i++)
        {
            dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]);
            dp[i][1]=max(dp[i-1][0]-prices[i],dp[i-1][1]);
        }
        return dp[n-1][0];
    }
};

121. 买卖股票的最佳时机

53. 最大子数组和

53. 最大子数组和

dp【i】:以i位置为结尾的所有子数组最大和

fen为长度为1和不为1的

dp【i】=max(dp【i-1】+nums【i】,nums【i】)

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n=nums.size();
        vector<int>dp(n);
        dp[0]=nums[0];
        for(int i=1;i<n;i++)
        {
            dp[i]=max(dp[i-1]+nums[i],nums[i]);
        }
        int ret=INT_MIN;
        for(int i=0;i<n;i++)
        {
            ret=max(ret,dp[i]);
        }
        return ret;
    }
};

918. 环形子数组的最大和

918. 环形子数组的最大和

借鉴环形打家劫舍

转化为非环形

f【i】:以i位置为结尾的所有子数组最大和

g【i】:以i位置为结尾的所有子数组最小和

返回 f【i】和sum-g【i】中最大的

注意:当全为负数时 f【i】为负

sum-g【i】为0,会返回0 这是错误的结果

class Solution {
public:
    int maxSubarraySumCircular(vector<int>& nums) {
        int n=nums.size();
        vector<int>f(n);
        vector<int>g(n);
        f[0]=nums[0];
        g[0]=nums[0];
        int sum=0;
        for(int i=1;i<n;i++)
        {
            f[i]=max(f[i-1]+nums[i],nums[i]);
            g[i]=min(g[i-1]+nums[i],nums[i]);
        }
        for(auto v:nums)
        {
            sum+=v;
        }
        int Max=f[0];
        
        for(int i=1;i<n;i++)
        {
            if(i==n-1&&sum-g[i]==0)
            {
                if(Max<f[i])
                    Max=f[i];
                break;
            }
            if(Max<f[i])
                Max=f[i];
            if(Max<(sum-g[i]))
                Max=sum-g[i];
        }
        return Max;
    }
};

乘积最大子数组

乘积最大子数组

f【i】以i为结尾的乘积最大的子数组

g【i】以i为结尾的乘积最小的子数组

f【i】=

1.nums【i】

2.

乘积要分类 nums【i】》0,要与f【i-1】相乘

nums【i】《0,要与g【i-1】相乘

f【i】=max(nums【i】,f【i-1】*nums【i】,g【i-1】*nums【i】)

g【i】=min(nums【i】,f【i-1】*nums【i】,g【i-1】*nums【i】)

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int n=nums.size();
        vector<vector<int>>dp(n,vector<int>(2));
        dp[0][0]=nums[0];
        dp[0][1]=nums[0];
        int Max=INT_MIN;
        for(int i=1;i<n;i++)
        {
            dp[i][0]=max(dp[i-1][0]*nums[i],max(nums[i],dp[i-1][1]*nums[i]));
            dp[i][1]=min(dp[i-1][1]*nums[i],min(nums[i],dp[i-1][0]*nums[i]));
            
        }
        for(int i=0;i<n;i++)
        {
            Max=Max>dp[i][0]?Max:dp[i][0];
        }
        return Max;
    }
};

乘积为正数的最长子数组长度

乘积为正数的最长子数组长度

f【i】以i为结尾乘积为正数的最长子数组长度

g【i】以i为结尾乘积为负数数的最长子数组长度

分类

1.nums【i】==0 f【i】==0

2.nums【i】<0

f【i】=g【i-1】+1   

要单独判断f【i-1】=0

g【i】=f【i-1】+1

2.nums【i】>0

要单独判断g【i-1】=0

 f【i】=f【i-1】+1   

g【i】=g【i-1】+1

class Solution {
public:
    int getMaxLen(vector<int>& nums) {
        int n=nums.size();
        vector<vector<int>>dp(n+1,vector<int>(2));
        dp[0][0]=0;
        dp[0][1]=-1;
        int Max=0;
        for(int i=1;i<=n;i++)
            if(nums[i-1]==0)
                dp[i][0]=dp[i][1]=0;
            else if(nums[i-1]<0)
            {
                if(dp[i-1][1]==0)
                    dp[i][0]=0;
                else
                    dp[i][0]=dp[i-1][1]+1;
                dp[i][1]=dp[i-1][0]+1;
            }
            else
            {
                dp[i][0]=dp[i-1][0]+1;
                if(dp[i-1][1]==0)
                    dp[i][1]=0;
                else
                    dp[i][1]= dp[i-1][1]+1;
            }
        for(int i=1;i<=n;i++)
            Max=Max>dp[i][0]?Max:dp[i][0];
        return Max;
    }
};

等差数列划分

等差数列划分

dp【i】以i位置为结尾的能构成等差数列的个数

dp【i】

1.nums【i】+nums【i-2】=nums【i-1】*2

dp【i】=1+dp【i-1】

2

dp【i】=0

class Solution {
public:
    int numberOfArithmeticSlices(vector<int>& nums) {
        int n=nums.size();
        vector<int>dp(n);
        if(n==1||n==2)
            return 0;
        dp[0]=dp[1]=0;
        for(int i=2;i<n;i++)
        {
            if(nums[i]+nums[i-2]==2*nums[i-1])
                dp[i]=1+dp[i-1];
            else
                dp[i]=0;
        }
        int sum=0;
        for(int i=0;i<n;i++)
        {
            sum+=dp[i];
        }
        return sum;
    }
};

最长湍流子数组

最长湍流子数组

f【i】表示以i为结尾的最长湍流子数组 且i位置为小

g【i】表示以i为结尾的最长湍流子数组 且i位置为大

f【i】=g【i】=1

if(nums【i-1】> nums【i】)

        f【i】=1+g【i-1】

else if (nums【i-1】<nums【i】)

        g【i】=1+f【i-1】

else 

        f【i】=g【i】=1

class Solution {
public:
    int maxTurbulenceSize(vector<int>& arr) {
        int n=arr.size();
        vector<int>f(n);
        vector<int>g(n);
        f[0]=g[0]=1;
        int Max=INT_MIN;
        for(int i=1;i<n;i++)
        {
            if(arr[i-1]>arr[i])
            {
                f[i]=g[i-1]+1;
                g[i]=1;
            }
            else if(arr[i-1]<arr[i])
            {
                g[i]=f[i-1]+1;
                f[i]=1;
            }
            else
            {
                f[i]=g[i]=1;
            }
            
        }
        for(int i=0;i<n;i++)
        {
            Max=Max>f[i]?Max:f[i];
            Max=Max>g[i]?Max:g[i];
        }
        return Max;
    }
};

  • 52
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值