动态规划训练营(leetcode)

本文精选四道经典算法题目,包括删除并获得点数、跳跃游戏II、零钱兑换及最大子序和,深入解析解题思路与算法实现,帮助读者掌握高效解题技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.删除并获得点数

题目描述: 

给你一个整数数组 nums ,你可以对它进行一些操作。

每次操作中,选择任意一个 nums[i] ,删除它并获得 nums[i] 的点数。之后,你必须删除 所有 等于 nums[i] - 1 和 nums[i] + 1 的元素。

开始你拥有 0 个点数。返回你能通过这些操作获得的最大点数

    

   

思路: 

注意到若 \textit{nums}nums 中不存在某个元素 xx,则选择任一小于 xx 的元素不会影响到大于 xx 的元素的选择。因此我们可以将 \textit{nums}nums 排序后,将其划分成若干连续子数组,子数组内任意相邻元素之差不超过 11。对每个子数组按照方法一的动态规划过程计算出结果,累加所有结果即为答案。

代码: 

    int rob4(vector<int>sum) {
            int n = sum.size();
        if (n == 1)
            return sum[0];
        if (n == 2)
            return max(sum[0], sum[1]);
        int first=sum[0],second=max(sum[0], sum[1]);
        for (int i = 2; i < n; i++) {
            int tmp = second;
            second = max(first + sum[i], tmp);
            first = tmp;
        }
        return max(first,second);
    }
    int deleteAndEarn(vector<int>& nums) {
        int n = nums.size();
        if (n == 1)
            return nums[0];
        sort(nums.begin(), nums.end());//排序
        int ans=0;
        //求相同元素的和到sum中
        vector<int>sum = { nums[0] };
        for (int i = 1; i < n; ++i) {
            int val = nums[i];
            if (val == nums[i - 1]) {
                sum.back() += val;
            } else if (val == nums[i - 1] + 1) {
                sum.push_back(val);
            } else {
                ans += rob4(sum);//防止短片
                sum = {val};//不断更新sum数组
            }
        }
        ans += rob4(sum);
        return ans;
    }

2.跳跃游戏II

 题目描述:

给你一个非负整数数组 nums ,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

你的目标是使用最少的跳跃次数到达数组的最后一个位置。

假设你总是可以到达数组的最后一个位置。

   

思路:  

如果我们「贪心」地进行正向查找,每次找到可到达的最远位置,就可以在线性时间内得到最少的跳跃次数。

例如,对于数组 [2,3,1,2,4,2,3],初始位置是下标 0,从下标 0 出发,最远可到达下标 2。下标 0 可到达的位置中,下标 1 的值是 3,从下标 1 出发可以达到更远的位置,因此第一步到达下标 1。

从下标 1 出发,最远可到达下标 4。下标 1 可到达的位置中,下标 4 的值是 4 ,从下标 4 出发可以达到更远的位置,因此第二步到达下标 4。

代码: 

    int jump(vector<int>& nums) {
        int n=nums.size();
        int step=0,maxPosition=0,end=0;
        for(int i=0;i<n-1;i++){
            maxPosition=max(maxPosition,i+nums[i]);
            if(i==end){//到达了end,开始再跳更新end
                end=maxPosition;
                step++;
            }
        }
        return step;
    }

3.零钱兑换

题目描述:

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。

你可以认为每种硬币的数量是无限的。

思路:

关键是动态规划方程的设计
我按我的思路一步步来吧
首先很容易想到使用dp[i]代表金额为i时,需要的最小硬币数量,最终求的是dp[amount]
然后试者填一下
dp = [-1]*(amount+1)
dp[0] = 0
dp[存在硬币的面值] = 1
如 coins = [1,2,5]
那dp[1],dp[2],dp[5] = 1,1,1
这些是初始条件,下面开始按顺序填表
在填写dp[i]的时候,其实就是假设在满足dp[i]的情况下,拿走一个硬币时需要的个数,然后+1
拿走硬币是什么意思呢?就是dp[i-coin],coin是选取的硬币的面值,如果dp[i-coin]!=-1,说明dp[i-coin]有解
那么dp[i-coin+coin]也就有解了,那么需要的硬币数量就是dp[i-coin]+1
但是有可能有多个解,这个时候就要找到最小的那个,即使用硬币最少的那个,所以最后状态转移方程就是这样的:
dp[i] = min(dp[i-coin[0]],dp[i-coin[1]]...,dp[i-coin[j]])+1
其中0-j属于dp[j]!=-1

代码实现:

    //动态规划dp[i]表示凑成i所需要的硬币个数 
    //dp[i]=min(dp[i-coins[0]],dp[i-coins[1],...,dp[i-coins[n-1]]])+1
    int dp[10050];//代表组合i元钱所需要的最小硬币数
    int coinChange(vector<int>& coins, int amount) {//就跟走楼梯一样
        sort(coins.begin(),coins.end());
        dp[0] = 0;
        int n=coins.size();
        for (int i = 1 ; i <= amount ;i++)
        { 
            dp[i] = INT_MAX;
            for (int j = 0 ; j < n;j++)
            {
                if(i-coins[j]>=0&&dp[i-coins[j]]!=INT_MAX)
                    dp[i] = min(dp[i],dp[i-coins[j]] + 1);
            }
        }
        return dp[amount] == INT_MAX ? -1:dp[amount];
    }

4.最大子序和

题目描述: 

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

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

思路和算法: 

 代码实现:

    //动态规划
    int maxSubArray(vector<int>& nums) {
        int pre = 0, maxAns = nums[0];
        for (int x : nums) {
            pre = max(pre + x, x);//小就跟更新
            maxAns = max(maxAns, pre);//同时记录最大的
        }
        return maxAns;
    }

5.环形子数组的最大和

题目描述: 

给定一个由整数数组 A 表示的环形数组 C,求 C 的非空子数组的最大可能和。

在此处,环形数组意味着数组的末端将会与开头相连呈环状。(形式上,当0 <= i < A.length 时 C[i] = A[i],且当 i >= 0 时 C[i+A.length] = C[i])

此外,子数组最多只能包含固定缓冲区 A 中的每个元素一次。(形式上,对于子数组 C[i], C[i+1], ..., C[j],不存在 i <= k1, k2 <= j 其中 k1 % A.length = k2 % A.length)。

代码:

    int maxSubarraySumCircular(vector<int>& nums) {
        int dp = nums[0], Max = dp, sum = dp, Min = 0;
        int n = nums.size();
        for (int i = 1; i < n; i++) {
            sum += nums[i];
            dp = nums[i] + max(dp, 0);
            Max = max(Max, dp);
        }
        dp = nums[0];
        for (int i = 1; i < n - 1; i++) {
            dp = nums[i] + min(0, dp);
            Min = min(dp, Min);
        }
        return max(sum - Min, Max);
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值