【leetcode】贪心问题刷题记录(一)
文章目录
前言
本文是基于leetcode贪心问题例题的学习记录
455. 分发饼干
链接
先从简单的开始,贪心问题顾名思义取最优,但是具体怎样做还是得从易到难慢慢积累
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s) {
sort(g.begin(), g.end());sort(s.begin(), s.end());
int ans = 0;
int size = s.size();
for(int i = g.size() - 1; i >= 0; i -- )
{
if(size > 0 && g[i] <= s[size - 1])
{
ans ++;
size --;
}
}
return ans;
}
};
376. 摆动序列
链接
一开始完全看错了,以为是连续序列
错误版本:
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
int flag = 0, last_number = nums[0];
int res = 0, ans = 0;
for(int i = 1; i < nums.size(); i ++ )
{
if(nums[i] > last_number)
{
if(flag == 0 || flag == -1)
{
last_number = nums[i];
flag = 1;
res ++;
}
else
{
last_number = nums[i];
ans = max(ans, res);
res = 0;
flag = 1;
}
}
else if(nums[i] < last_number)
{
if(flag == 0 || flag == 1)
{
last_number = nums[i];
flag = -1;
res ++;
}
else
{
last_number = nums[i - 1];
ans = max(ans, res);
res = 0;
flag = -1;
}
}
else
{
ans = max(ans, res);
last_number = nums[i - 1];
res = 0;
flag = 0;
}
}
return ans;
}
};
正确版本:
题解
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
if(nums.size() <= 1)return nums.size();
int pre = 0, cur = 0;
int ans = 1;
for(int i = 0; i < nums.size() - 1; i ++ )
{
int cur = nums[i + 1] - nums[i];
if((pre <= 0 && cur > 0) || (pre >= 0 && cur < 0))
{
ans ++;
pre = cur;
}
}
return ans;
}
};
53. 最大子数组和
链接
做过的题,先用动态规划做一次
class Solution {
public:
int maxSubArray(vector<int>& nums) {
vector<int> dp(nums.size(), 0);
dp[0] = nums[0];
int ans = dp[0];
for(int i = 1; i < nums.size(); i ++ )
{
dp[i] = max(dp[i - 1] + nums[i], nums[i]);
ans = max(ans, dp[i]);
}
return ans;
}
};
从dp也能透到一些贪心的做法
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int ans = 0, res = 0;
for(int num : nums)
{
res += num;
if(res < 0)res = 0;
ans = max(ans, res);
}
return ans;
}
};
第一次写的有些过不了,忘了数组里全是负数的情况,修改之后通过
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int ans = INT_MIN, res = 0;
for(int num : nums)
{
res += num;
ans = max(ans, res);
if(res < 0)res = 0;
}
return ans;
}
};
122. 买卖股票的最佳时机 II
链接
还是先用dp做一遍
class Solution {
public:
int maxProfit(vector<int>& prices) {
int size = prices.size();
if(size == 0)return 0;
vector<vector<int>> dp(size, vector<int>(2));
dp[0][0] = 0;
dp[0][1] = - prices[0];
for(int i = 1; i < size; i ++ )
{
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]);
}
return dp[size - 1][0];
}
};
贪心的做法是:只要涨价了就卖
class Solution {
public:
int maxProfit(vector<int>& prices) {
int ans = 0;
for(int i = 0; i < prices.size() - 1; i ++ )
ans += max(prices[i + 1] - prices[i], 0);
return ans;
}
};
55. 跳跃游戏
链接
跟上一题很像,把连续的一段拆成小段,把跳三格拆成跳三次一格
class Solution {
public:
bool canJump(vector<int>& nums) {
if(nums.size() <= 1)return true;
int steps = 0;
for(auto num : nums)
{
steps --;
if(num > steps)steps = num;
if(steps == 0)return false;
}
return true;
}
};
这一个解法有错误,忽略了刚好跳完的情况
正确写法:
class Solution {
public:
bool canJump(vector<int>& nums) {
if(nums.size() <= 1)return true;
int steps = 0;
for(int i = 0; i < nums.size(); i ++ )
{
steps --;
if(nums[i] > steps)steps = nums[i];
if(steps == 0 && i != nums.size() - 1)return false;
}
return true;
}
};
45. 跳跃游戏 II
链接
自己写的,思路错了,只过了一半
class Solution {
public:
int jump(vector<int>& nums) {
if(nums.size() <= 1)return 0;
int steps = 0;
int ans = 0;
for(int i = 0; i < nums.size(); i ++ )
{
steps --;
if(i + steps + 1 >= nums.size())break;
if(nums[i] > steps)
{
steps = nums[i];
ans ++;
}
}
return ans;
}
};
正确的解法是,只有当走到底走不了的时候才加一步,所以在之前就要保存多走一步能到达的最远距离
题解
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;
}
};
// 版本二
class Solution {
public:
int jump(vector<int>& nums) {
int curDistance = 0; // 当前覆盖的最远距离下标
int ans = 0; // 记录走的最大步数
int nextDistance = 0; // 下一步覆盖的最远距离下标
for (int i = 0; i < nums.size() - 1; i++) { // 注意这里是小于nums.size() - 1,这是关键所在
nextDistance = max(nums[i] + i, nextDistance); // 更新下一步覆盖的最远距离下标
if (i == curDistance) { // 遇到当前覆盖的最远距离下标
curDistance = nextDistance; // 更新当前覆盖的最远距离下标
ans++;
}
}
return ans;
}
};
34. 加油站
链接
写得有些冗长了
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int sum = 0;
for(int i = 0; i < gas.size(); i ++ )
{
gas[i] -= cost[i];
sum += gas[i];
}
if(sum < 0)return -1;
int ans = 0, res = 0;
int size = gas.size();
for(int i = 0; i < 2 * size; i ++ )
{
res += gas[i % size];
if(res < 0)
{
res = 0;
ans = (i % size) + 1;
}
}
return ans;
}
};
看看题解
题解
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int curSum = 0;
int totalSum = 0;
int start = 0;
for (int i = 0; i < gas.size(); i++) {
curSum += gas[i] - cost[i];
totalSum += gas[i] - cost[i];
if (curSum < 0) { // 当前累加rest[i]和 curSum一旦小于0
start = i + 1; // 起始位置更新为i+1
curSum = 0; // curSum从0开始
}
}
if (totalSum < 0) return -1; // 说明怎么走都不可能跑一圈了
return start;
}
};
明显简化了很多,学习学习~
135. 分发糖果
链接
枯了,又是看错了题,看成相邻高几分就多分多少糖果
错误代码:
class Solution {
public:
int candy(vector<int>& ratings) {
vector<int> res(ratings.size(), 0);
int m = INT_MAX;
for(int i = 1; i < ratings.size(); i ++ )
{
res[i] = ratings[i] - ratings[i - 1];
m = min(m, res[i]);
}
int ans = 0;
for(auto rate : res)
{
ans += 1 + rate - m;
cout << ans << ' ';
}
return ans;
}
};
正确解法:
class Solution {
public:
int candy(vector<int>& ratings) {
vector<int> candyVec(ratings.size(), 1);
for (int i = 1; i < ratings.size(); i++)
if (ratings[i] > ratings[i - 1])
candyVec[i] = candyVec[i - 1] + 1;
for (int i = ratings.size() - 2; i >= 0; i--)
if (ratings[i] > ratings[i + 1] )
candyVec[i] = max(candyVec[i], candyVec[i + 1] + 1);
int result = 0;
for (int i = 0; i < candyVec.size(); i++) result += candyVec[i];
return result;
}
};
860.柠檬水找零
class Solution {
public:
bool lemonadeChange(vector<int>& bills) {
int five = 0, ten = 0, twenty = 0;
for (int bill : bills) {
// 情况一
if (bill == 5) five++;
// 情况二
if (bill == 10) {
if (five <= 0) return false;
ten++;
five--;
}
// 情况三
if (bill == 20) {
// 优先消耗10美元,因为5美元的找零用处更大,能多留着就多留着
if (five > 0 && ten > 0) {
five--;
ten--;
twenty++; // 其实这行代码可以删了,因为记录20已经没有意义了,不会用20来找零
} else if (five >= 3) {
five -= 3;
twenty++; // 同理,这行代码也可以删了
} else return false;
}
}
return true;
}
};
406. 根据身高重建队列
class Solution {
public:
static bool cmp(const vector<int>& a, const vector<int>& b) {
if (a[0] == b[0]) return a[1] < b[1];
return a[0] > b[0];
}
vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
sort (people.begin(), people.end(), cmp);
vector<vector<int>> que;
for (int i = 0; i < people.size(); i++) {
int position = people[i][1];
que.insert(que.begin() + position, people[i]);
}
return que;
}
};
435. 无重叠区间
链接
这种区间问题要么就是排序头要么就是排序尾
class Solution {
public:
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
if (intervals.size() == 0) return 0;
sort(intervals.begin(), intervals.end(), [](const auto& a, const auto& b){
return a[1] < b[1];
});
int cnt = 1;
int end = intervals[0][1];
for(int i = 1; i < intervals.size(); i ++ )
{
if(intervals[i][0] >= end)
{
end = intervals[i][1];
cnt++;
}
}
return intervals.size() - cnt;
}
};
今天就先练到这里了,贪心问题其实也是熟能生巧,多练多积累就会了。吃月饼去咯,中秋快乐~