1. 简单贪心算法(常识问题)
到底什么是贪心算法:利用局部最优来推出全局最优就是核心
1.1 LeetCode第455题—分发饼干
局部最优就是大饼干喂给胃口大的,充分利用饼干尺寸喂饱一个,全局最优就是喂饱尽可能多的小孩
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s) {
sort(g.begin(),g.end());
sort(s.begin(),s.end());
int index = s.size()-1;
int num = 0;
for(int i = g.size()-1;i>=0;--i)
{
if(index >= 0 && s[index] >= g[i])
{
num++;
index--;
}
}
return num;
}
};
1.2 LeetCode第1005题—K此取反后最大化的数组和
class Solution {
static bool cmp(int a,int b)
{
return abs(a) > abs(b);
}
public:
int largestSumAfterKNegations(vector<int>& nums, int k) {
sort(nums.begin(),nums.end(),cmp);
for(int i = 0;i<nums.size();++i)
{
if(nums[i] < 0 && k > 0)
{
nums[i] *= -1;
k--;
}
}
while(k--)
{
nums[nums.size()-1] *= -1;
}
int ret = 0;
for(int& e : nums)
{
ret += e;
}
return ret;
}
};
1.2 LeetCode第860题—柠檬水找零
因为5元既可以给10元的找钱,也可以给20元的找钱,但是10元的只能够给20元的找钱,所以我们尽可能的,在找20元的时候,优先把10元的找出去,这样才能够尽可能的给每一个人都把钱找了
class Solution {
public:
bool lemonadeChange(vector<int>& bills) {
int five = 0,ten = 0;
for(auto& bill : bills)
{
if(bill == 5)
{
five++;
}
else if(bill == 10)
{
if(five < 0)
return false;
ten++;
five--;
}
else
{
if(five > 0 && ten > 0)
{
five--;
ten--;
}
else if(five >= 3)
{
five -= 3;
}
else
{
return false;
}
}
}
return true;
}
};
2. 中等贪心算法
2.1 LeetCode第376题—摆动序列
有可能是会出现[3,3,3,5]的情况的,所以这里就相乘波峰和波谷交替来增加最长的摆动序列。
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
int n = nums.size();
if (n < 2)
return n;
int curdiff = 0;
int prevdiff = 0;
int result = 1;
for(int i = 0;i<n-1;++i)
{
curdiff = nums[i+1] - nums[i];
if((curdiff >0 && prevdiff<= 0) || (curdiff < 0 && prevdiff>= 0))
{
result++;
prevdiff = curdiff;
}
}
return result;
}
};
2.2 LeetCode第122题—买卖股票的最佳时机II
- 局部最优:收集每天的正利润,全局最优:求得最大利润.
class Solution {
public:
int maxProfit(vector<int>& prices) {
int ret = 0;
for(int i = 1;i<prices.size();++i)
{
ret += std::max(prices[i] - prices[i-1],0);
}
return ret;
}
};
3. LeetCode第435题—无重叠区间
我们依旧按照右边界进行排序,来记录要删除的区间个数
class Solution {
public:
static bool cmp(vector<int>& a, vector<int>& b)
{
return a[1] < b[1];
}
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
sort(intervals.begin(),intervals.end(),cmp);
int del = 0;
int t = intervals[0][1];
for(int i = 1;i<intervals.size();++i)
{
if(intervals[i][0] < t)
del++;
else
t = intervals[i][1];
}
return del;
}
};
4. LeetCode第406题—根据身高建队列
LeetCode题目链接:https://leetcode-cn.com/problems/queue-reconstruction-by-height/ 其实就是把这个调整到合适的位置,其实这道题还是挺有意思的。 排序,根据身高,然后对于身高相同的,就根据第二个参数进行排序,对于第二个参数也就是每个需要插入的位置,所以我们就拿到这个位置,然后将对应的vector< int >插入其中,但是使用vector这里就会效率很慢,所以最好的方式在插入的部分,我们使用list来解决
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>> res;
list<vector<int>> que;
for (int i = 0; i < people.size(); i++) {
int position = people[i][1];
std::list<vector<int>>::iterator it = que.begin();
while(position--)
it++;
que.insert(it,people[i]);
}
return vector<vector<int>>(que.begin(),que.end());
}
};
使用vector来解决
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>> res;
for (int i = 0; i < people.size(); i++) {
int position = people[i][1];
res.insert(res.begin() + position, people[i]);
}
return res;
}
};
5. LeetCode第763题—划分字母区间
LeetCode题目链接:https://leetcode-cn.com/problems/partition-labels/ 解题思路:
class Solution {
public:
vector<int> partitionLabels(string s) {
vector<int> hash(26,0);
for(int i = 0;i<s.size();++i)
hash[s[i]-'a'] = i;
vector<int> v;
int left = 0;
int right = 0;
for(int i = 0;i<s.size();++i)
{
right = max(right,hash[s[i]-'a']);
if(i == right)
{
v.push_back(right-left+1);
left = right+1;
}
}
return v;
}
};
6. LeetCode第452题—用最少数量的箭引爆气球
LeetCode题目链接:https://leetcode-cn.com/problems/minimum-number-of-arrows-to-burst-balloons/ 这道题就是沿着y轴射出去一支箭,对于只要穿过X的begin到end的任意一个位置都一样将该气球射穿,那么我们这里维护一个右边界,简单点说就是:在x等于6的地方沿着y轴射出一支箭,那么这一支箭,将会使气球1和气球2击穿,当下一个气球的左边界还要大于所维护的最小右边界的时候,说明此时这个气球无法做到和前面的气球同时被击穿,所以需要新增加一支箭
class Solution {
public:
static bool cmp(vector<int>& a , vector<int>& b)
{
return a[0] < b[0];
}
int findMinArrowShots(vector<vector<int>>& points) {
if(points.size() == 0)
return 0;
sort(points.begin(),points.end(),cmp);
int num = 1;
int minRight = points[0][1];
for(int i = 1;i<points.size();++i)
{
if(points[i][0] > minRight)
{
num++;
minRight = points[i][1];
}
else
{
minRight = std::min(minRight,points[i][1]);
}
}
return num;
}
};
7. LeetCode第134题—加油站
LeetCode题目链接:https://leetcode-cn.com/problems/gas-station/ 解题思路: 从局部最优,去推导全局最有的情况
- 你要确保从i位置出发,能够开到i+1的位置(如果从[0,i],发现在第i处的时候,为负了,那么起始位置就应该设置为i+1,为什么呢?起始可以抽象为A,B两个点的,把[0,i]这一段都抽象为A,那么A->B是过不去了,那么下一个起始位置就应该设置为下一个点)
- 所有站里的油总量要>=车子的总耗油量
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int start = 0;
int curSum = 0,tatalSum = 0;
for(int i = 0;i<gas.size();++i)
{
curSum += gas[i] - cost[i];
tatalSum += gas[i] - cost[i];
if(curSum < 0)
{
start = i+1;
curSum = 0;
}
}
return tatalSum >= 0 ? start:-1;
}
};
8. LeetCode第135题—分发糖果
LeetCode题目链接:https://leetcode-cn.com/problems/candy/
- 从左边往右边遍历,来对糖果的数量进行求解
- 从右边往左边遍历,这里是会出现一点小问题的,此时对于糖果的数组来说,每一个位置都是满足右边的大于左边的糖果数,但是此时如果对于左边的大于右边的分数时,我们简单的让v[i] = v[i+1] + 1;那么这一步是不对的,因为他会破坏第一个条件,所以我们需要做的就是,两个中取较大值。
class Solution {
public:
int candy(vector<int>& ratings) {
int n = ratings.size();
vector<int> v(n,1);
for(int i = 1;i<n;++i)
{
if(ratings[i] > ratings[i-1])
v[i] = v[i-1] +1;
}
for(int i = n-2;i>=0;--i)
{
if(ratings[i] > ratings[i+1])
v[i] = max(v[i],v[i+1] + 1);
}
int res = 0;
for(int& num : v)
res += num;
return res;
}
};
9. LeetCode第55题—跳跃游戏I
LeetCode题目链接:https://leetcode-cn.com/problems/jump-game/ 就看能否覆盖的面积达到数组的最后一个位置。
class Solution {
public:
bool canJump(vector<int>& nums) {
int cover = nums[0];
for(int i = 0;i<=cover;++i)
{
cover = std::max(i+nums[i],cover);
if(cover >= nums.size()-1)
return true;
}
return false;
}
};
9.1 LeetCode第55题—跳跃游戏II
LeetCode题目链接:https://leetcode-cn.com/problems/jump-game-ii/ 那么这道题和第一道跳跃游戏究竟有什么不同呢? 其实这是一道贪心算法+动态规划的题目
- 如果当前覆盖最远距离下标不是是集合终点,步数就加一,还需要继续走。
- 如果当前覆盖最远距离下标就是是集合终点,步数不用加一,因为不能再往后走了。(所以条件判断里面才会写nums.size()-1)
class Solution {
public:
int jump(vector<int>& nums) {
int maxfar = 0;
int curfar = 0;
int step = 0;
for(int i = 0;i<nums.size()-1;++i)
{
maxfar = max(maxfar,i+nums[i]);
if(i == curfar)
{
curfar = maxfar;
step++;
}
}
return step;
}
};
10. LeetCode第738题—单调递增的数字
LeetCode题目链接:https://leetcode-cn.com/problems/monotone-increasing-digits/ 例如:98,一旦出现strNum[i - 1] > strNum[i]的情况(非单调递增),首先想让strNum[i - 1]–,然后strNum[i]给为9,这样这个整数就是89,即小于98的最大的单调递增整数。
这一点如果想清楚了,这道题就好办了。
局部最优:遇到strNum[i - 1] > strNum[i]的情况,让strNum[i - 1]–,然后strNum[i]给为9,可以保证这两位变成最大单调递增整数。
class Solution {
public:
int monotoneIncreasingDigits(int N) {
string strNum = to_string(N);
int flag = 0;
for (int i = strNum.size() - 1; i > 0; i--) {
if (strNum[i - 1] > strNum[i] ) {
flag = i;
strNum[i - 1]--;
}
}
for (int i = flag; i < strNum.size(); i++) {
strNum[i] = '9';
}
return stoi(strNum);
}
};
|