算法训练1-day24-贪心

30 阅读4分钟
  1. 122. 买卖股票的最佳时机 II 贪心:题目要求我们最多只能持有一只股票,但一天之内可以多次买卖,也就意味着 prices[3]-prices[0] = (prices[3] - prices[2]) + (prices[2] - prices[1]) + (prices[1] - prices[0]) 因此,我们可以将长间隔的两天的利润分解为各个相邻两天的利润和,进一步的,我们可以只看相邻天数的利润,并且只统计利润为正的结果,最终得到的就是最大利润

动态规划:dp数组的定义为dp[i].first表示第i天持有股票

AC代码:

// 动态规划
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        vector<pair<int, int>> dp(prices.size());
        // first-当天有股票
        dp[0].first = -prices[0];
        // second-当天没有股票
        dp[0].second = 0;
        for (int i = 1; i < prices.size(); ++i) {
            dp[i].first = max(dp[i - 1].first, dp[i - 1].second - prices[i]);
            dp[i].second = max(dp[i - 1].second, dp[i - 1].first + prices[i]);
        }

        return dp[prices.size() - 1].second;
    }
};
// 贪心
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int ans = 0;
        int val = 0;
        for (int i = 1; i < prices.size(); ++i) {
            val = prices[i] - prices[i - 1];
            if (val > 0) ans += val;
        }

        return ans;
    }
};
  1. 55. 跳跃游戏 记录当前还能往前跳的距离,如果当前还能往前的距离加上当前位置要大于整个数组的长度,那么就能到结尾;如果当前还有往前跳的距离小于0(注意,不是等于0,因为有可能是[1,2,3]的情况,此时第一步在i=0的时候step会减到0,但按照这份逻辑这是正确地),且当前位置不是最后一位,那么就代表不能走到最后 AC代码:
class Solution {
public:
    bool canJump(vector<int>& nums) {
        if (nums.size() <= 1) return true;
        int step = 0;
        for (int i = 0; i < nums.size(); ++i) {
            step = max(step, nums[i]);
            if (step + i + 1 >= nums.size())
                return true;
            step--;
            if (step < 0 && i != nums.size() - 1) {
                return false;
            }
        }
        return false;
    }
};
  1. 45. 跳跃游戏 II

代码如下:

class Solution {
public:
    int jump(vector<int>& nums) {
        if (nums.size() <= 1)
            return 0;
        vector<int> jumpStep(nums.size(), 0);
        jumpStep[0] = nums[0];
        int count = 0;
        for (int i = 1; i < nums.size(); ++i) {
            if (i > jumpStep[count]) {
                count++;
            }
            if (nums[i] + i > jumpStep[count + 1]) {
                jumpStep[count + 1] = nums[i] + i;
            }
        }
        return count + 1;
    }
};

// 版本一
class Solution {
public:
    int jump(vector<int>& nums) {
        if (nums.size() == 1) return 0;
        int curDistance = 0;    // 当前覆盖最远距离下标
        int ans = 0;            // 记录走的最大步数
        int nextDistance = 0;   // 下一步覆盖最远距离下标
        for (int i = 0; i < nums.size(); i++) {
            nextDistance = max(nums[i] + i, nextDistance);  // 更新下一步覆盖最远距离下标
            if (i == curDistance) {                         // 遇到当前覆盖最远距离下标
                ans++;                                  // 需要走下一步
                curDistance = nextDistance;             // 更新当前覆盖最远距离下标(相当于加油了)
                if (nextDistance >= nums.size() - 1) break;  // 当前覆盖最远距到达集合终点,不用做ans++操作了,直接结束
            }
        }
        return ans;
    }
};
  1. 1005. K 次取反后最大化的数组和

第一种想法:先排序,然后依次将负数取反,然后依照负数与正数以及k的情况分类讨论

第二种想法:

  • 第一步:将数组按照绝对值大小从大到小排序,注意要按照绝对值的大小
  • 第二步:从前向后遍历,遇到负数将其变为正数,同时K--
  • 第三步:如果K还大于0,那么反复转变数值最小的元素,将K用完
  • 第四步:求和
class Solution {
public:
    int largestSumAfterKNegations(vector<int>& nums, int k) {
        sort(nums.begin(), nums.end());
        int reversePos = 0;
        int sum = 0;
        int diff = 0;
        for (int i = 0; i < k; i++) {
            if (nums[reversePos] < 0) {
                nums[reversePos] = -nums[reversePos];
                // 既有正数又有负数的情况,选数值最小的那个变化
                // nums[reversePos]变为正数后依然小于nums[reversePos + 1]
                // 表明nums[reversePos + 1]也是大于0的
                // 此时选这两者中最小的那个
                if (reversePos + 1 < nums.size() &&
                    nums[reversePos + 1] > nums[reversePos]) {
                    if ((k - i - 1) % 2 == 1) {
                        diff = 2 * nums[reversePos];
                    }
                    break;
                }
                reversePos++;
                // 全部是负数的情况
                if (reversePos >= nums.size()) {
                    if ((k - i - 1) % 2 == 1) {
                        diff = 2 * nums[reversePos - 1];
                    }
                    break;
                }
            } else if (nums[reversePos] == 0) {
	            //nums[reversePos] == 0可以整合进下面的else
                break;
            } else {
	            //nums[reversePos] > 0,如果k还有剩余且为奇数,
	            //那么最后的结果要减去2倍这个最小值
                if ((k - i) % 2 == 1) {
                    diff = 2 * nums[reversePos];
                }
                break;
            }
        }
        sum = accumulate(nums.begin(), nums.end(), 0) - diff;
        return sum;
    }
};

// 按照绝对值排序
class Solution {
static bool cmp(int a, int b) {
    return abs(a) > abs(b);
}
public:
    int largestSumAfterKNegations(vector<int>& A, int K) {
        sort(A.begin(), A.end(), cmp);       // 第一步
        for (int i = 0; i < A.size(); i++) { // 第二步
            if (A[i] < 0 && K > 0) {
                A[i] *= -1;
                K--;
            }
        }
        if (K % 2 == 1) A[A.size() - 1] *= -1; // 第三步
        int result = 0;
        for (int a : A) result += a;        // 第四步
        return result;
    }
};