LeetCode第6&7天 | 动态规划 | 20220723&20220724

LeetCode第6&7天 | 动态规划 | 20220723&20220724

动态规划

本文章参考了许多他人的笔记,仅供自己学习复习使用。
leetcode官网



139. 单词拆分

1.1 读题

139. 单词拆分

1.2 解题

#include <unordered_set>

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string>wordSet;
        for(int i = 0; i < wordDict.size(); i++) {
            wordSet.insert(wordDict[i]);
        }
        int len = s.length();

        // 状态定义: 以s[i]结尾的子字符串是否符合题意
        vector<bool> dp(len);
        dp[0] = true;
        for(int right = 0; right < len; right++){
           
            // 分类讨论1-不拆分
            if(wordSet.count(s.substr(0, right+1))){
                dp[right] = true;
                continue;
            }

            // 分类讨论2-拆分
            for(int left = right-1;left >= 0; left--){
                if(wordSet.count(s.substr(left+1,right+1)) && dp[left]){
                    dp[right] = true;
                    break;
                }
            }
        }
        
        return dp[len-1];
    }
};

152.乘积最大子序列(中等)

2.1 读题

152.乘积最大子序列(中等)

2.2 解题

参考:liweiwei的解答 思路按照liweiwei的,改写了c++代码。以下记录仅供自己学习复习使用。

  1. 第一步:状态定义
    数组的动态规划问题、子序列、连续子序列的一个通用的状态设置是:
    以索引 i 结尾的连续子序列的乘积的最大值,用dp[i]表示

  2. 第二步:推导状态转移方程
    把整个 dp 数组看一遍求最大值即可。因此状态转移方程可能是:
    dp[i] = max(dp[i - 1] * nums[i], nums[i])
    说明:牢记状态的设置,一定以索引 i 结尾,即乘积数组中 nums[i] 必须被选取
    但本题,dp[i-1]可能为正数或负数,nums[i]也可能为正数或负数,会导致情况十分复杂,数学经验告诉我们这里需要分类讨论,其实就是在提醒我们状态不够用了。因此,需要在原来的一维 dp 后面新增一个状态:
    dp[i][1] 表示:以 nums[i] 结尾的连续子序列的乘积的最值;
    dp[i][0] 表示:以 nums[i] 结尾的连续子序列的乘积的最值。

  3. 第三步:思考初始化
    最开始肯定要算上第一个数,nums[0]本身即为初试的最大值。
    所以在函数最开始要补充:当nums数组长度为0时,函数直接返回0。

  4. 第四步:思考输出
    不知道怎么讲,粗鄙的说法就是整个动态规划的过程是断开的,并不像买卖股票系列一直串起来,最后直接输出最后一个元素的值即可。
    本题还需要再动态规划结束后,遍历一次dp数组,找出max{dp[i][1]}(i = 1,2,3…n-1)。

代码2.1

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int len = nums.size();
        if(len == 0) return 0;

        // definition of status
        vector<vector<int>> dp(len,vector<int>(2));

        // initialization:
        dp[0][0] = nums[0];
        dp[0][1] = nums[0];

        for(int i=1;i<len;i++){
            if(nums[i]>=0){
                dp[i][1]= max(dp[i-1][1]*nums[i],nums[i]);
                dp[i][0] = min(dp[i-1][0]*nums[i],nums[i]);
            }else{
                dp[i][1] = max(dp[i-1][0]*nums[i],nums[i]);
                dp[i][0] = min(dp[i-1][1]*nums[i],nums[i]);
            }
        }

        
        int res = dp[0][1];
        for(int i =1; i<len;i++){
            res = max(res,dp[i][1]);
        }
        return res;
        
        
    }
};

  1. 第五步:思考空间优化
    【因为】当天只与i-1的状态有关,【所以】考虑滚动数组

代码2.2(待修正)

这两天好奇怪,怎么总是手动模拟没问题,但就是会解答出错…
解答出错的示例为nums = [-4,-3,-2] 希望能早日得到解决。

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int len = nums.size();
        if(len == 0) {
            return 0;
        }
        // definition of status
        vector<vector<int>> dp(len,vector<int>(2));

        // initialization:
        dp[0][0] = nums[0];
        dp[0][1] = nums[0];

        int res = -2*100000;
        int flag = 0;
        for(int i=1;i<len;i++){
            flag = 1;
            if(nums[i]>=0){
                dp[i/2][1]= max(dp[(i-1)/2][1]*nums[i],nums[i]);
                dp[i/2][0] = min(dp[(i-1)/2][0]*nums[i],nums[i]);
                
            }else{
                dp[i/2][1] = max(dp[(i-1)/2][0]*nums[i],nums[i]);
                dp[i/2][0] = min(dp[(i-1)/2][1]*nums[i],nums[i]);
            }
            if(dp[i/2][1]>res){
                    res = dp[i/2][1];
                }
        }

        if(!flag){
            res = nums[0];
        }
        return res;
        
        
    }
};

打家劫舍系列

198.打家劫舍

198.打家劫舍

代码1:定义成一维表格的动态规划

class Solution{
public:
    int rob(vector<int>& nums){
        if(nums.empty()){
            return 0;
        }
        int n = nums.size();
        if(n ==1){
            return nums[0];
        }
        
        vector<int> dp(n);
        // 初始化
        // 只有一间房,则必偷
        dp[0]=nums[0];
        // 第二间:若偷,则nums[1]; 若不偷,则dp[0]
        dp[1]=max(nums[1], dp[0]);
        
        

        for(int i = 2;i<n;i++){
            //动态转移:
            // 若偷i,则不偷i-1,为dp[i-2]+nums[i]
            // 若不偷i,则直接按dp[i-1](不需要管到底i-1间偷了没偷)
            dp[i] = max(dp[i-2]+nums[i], dp[i-1]);
        }

        return dp[n-1];
    }
};

代码2:定义成二维表格的动态规划

liweiwei的思路:设置哨兵并进行偏移

class Solution{
public:
    int rob(vector<int>& nums){
        if(nums.empty()){
            return 0;
        }
        int len = nums.size();
        if(len ==1){
            return nums[0];
        }
        
        // 0 表示不偷
        // 1 表示偷
        // 多加1天表示哨兵,相应地要做一些偏移
        vector<vector<int>> dp(len+1,vector<int>(2));

        for(int i = 1; i<len+1;i++){
            // 不偷
            dp[i][0] = max(dp[i-1][0], dp[i-1][1]);

            // 偷
            // 注意这里有个偏移
            dp[i][1] = dp[i-1][0] + nums[i-1];
        }
        return max(dp[len][0],dp[len][1]);

    }
};


213. 打家劫舍 II(中等)

213. 打家劫舍 II(中等)

这题比打家劫舍多了一个房子们首尾相连。触动警报的方式还是一样。

思路:考虑将n个房间编号为0~n-1
1)若不偷n,则可对[0,1,…,n-2]进行198.打家劫舍一样的解答;
2)若不偷0,则可对[1,2,…,n-1]进行198.打家劫舍一样的解答;

对以上两种情况分别进行198.打家劫舍,然后取两者的最大值,即为最终答案。

代码1:

class Solution {
public:
    int rob(vector<int>& nums) {
        int n = nums.size();
        if(nums.empty())
            return 0;
        else if(nums.size() == 1)
            return nums[1];
        else if(nums.size() ==2)
            return max(nums[0],nums[1]);

        vector<int> dp(n);

        int start_i=0, end_i = n-2;
        dp[0] = nums[start_i];
        dp[1] = max(dp[0], nums[start_i + 1]);
        for(int i = start_i + 2; i<= end_i; i++){
            dp[i]=max(dp[i-2]+nums[start_i + i], dp[i-1]);
        }

        int temp = dp[end_i];

        dp = vector<int>(nums.size());
        start_i=1; end_i = n-1;
        dp[0] = nums[start_i];
        dp[1] = max(dp[0], nums[start_i + 1]);
        for(int i = start_i + 2; i<= end_i; i++){
            dp[i]=max(dp[i-2]+nums[start_i + i], dp[i-1]);
        }

        return max(temp, dp[end_i]);
    }
};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值