- 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;
}
};
- 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;
}
};
代码如下:
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;
}
};
第一种想法:先排序,然后依次将负数取反,然后依照负数与正数以及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;
}
};