贪心算法-

题目链接:455. 分发饼干 - 力扣(Leetcode)

// class Solution {
// public:
//     int findContentChildren(vector<int>& g, vector<int>& s) {
//         // 看来我没有慧根,我居然觉得从小的开始分才是最优解……
//         // 哦每个孩子最多只能给一块,那就不能两块相加了
//         // 那就从大的分吧
//         int res=0;
//         sort(g.begin(),g.end());
//         sort(s.begin(),s.end());
//         int sIndex=s.size()-1;
//         // 从大到小遍历
//         for(int i=g.size()-1;i>=0;i--){
//             // 从s、g中最大的开始,如果s最大的都比g最大的小,那么这几个不可能被满足,遍历直到s可以满足g的那个开始
//             if(sIndex>=0&&s[sIndex]>=g[i]){
//                 res++;
//                 sIndex--;
//             }
//         }
//         return res;
//     }
// };

class Solution {
public:
    int findContentChildren(vector<int>& g, vector<int>& s) {
        //也可以从小的开始分,把小的都分完
        //感觉不如先分大的合理啊,后面不会剩余大的但前面小的没分到的吗
        int res=0;
        sort(g.begin(),g.end());
        sort(s.begin(),s.end());
        int gIndex=0;
        for(int i=0;i<s.size();i++){
            if(gIndex<g.size()&&g[gIndex]<=s[i]){
                res++;
                gIndex++;
            }
        }
        return res;
    }
};

题目链接:376. 摆动序列 - 力扣(Leetcode)

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        // 包含所有峰值的数组就是摆动序列的最大长度
        // 这我真想不到
        // 基本属于想不到也看不懂,他大爷的
        if(nums.size()<=1){//只有一个元素
            return nums.size();
        }
        int curDiff=0;//当前一对的差值
        int preDiff=0;//前一对的差值
        int res=1;//记录峰值个数
        for(int i=0;i<nums.size()-1;i++){
            curDiff=nums[i+1]-nums[i];//记录当前差值
            // 出现峰值
            if((preDiff<=0&&curDiff>0)||(preDiff>=0&&curDiff<0)){
                res++;
                preDiff=curDiff;
            }
        }
        return res;
    }
};

题目链接:53. 最大子数组和 - 力扣(Leetcode)

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        // 肯定要从正数开始到正数结束
        // 当负数后面的正数比负数绝对值大的时候就加入进来,否则不加入【不对!!!】
        // 当和为负数时抛弃前面的重新开始
        int curSum=0,maxSum=INT32_MIN;
        for(int i=0;i<nums.size();i++){
            // 不必非要等到nums[i]>0才相加,加上之后如果小于0就置0抛弃
            curSum+=nums[i];
            if(curSum>maxSum){
                maxSum=curSum;
            }
            // 如果当前和小于0,则抛弃前面的置0重新开始
            if(curSum<0){
                curSum=0;
            }
        }
        return maxSum;
    }
};

题目链接:122. 买卖股票的最佳时机 II - 力扣(Leetcode)

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        // 我从上一道题 最大子数组和 中得到的灵感
        // 计算每两天之间的利润,本来想跟上一题一样,看连续子数组最大值,但后来发现完全不用连续,只要把正利润都加起来就行了,奈斯
        int result = 0;
        for (int i = 1; i < prices.size(); i++) {
            result += max(prices[i] - prices[i - 1], 0);
        }
        return result;
    }
};

题目链接:55. 跳跃游戏 - 力扣(Leetcode)

class Solution {
public:
    bool canJump(vector<int>& nums) {
        // 我又没有常识了好烦啊啊啊啊
        // 看了答案果然我没有常识啊啊啊啊啊
        // 不必纠结于到底跳到第几个,而是看能不能跳到最后一个
        int res=0;
        if(nums.size()==1)  return true;
        // 注意这里,i<=能到达的最大下标
        // 而不是遍历整个数组,太巧妙了我就是个傻子
        for(int i=0;i<=res;i++){
            res=max(nums[i]+i,res);//能不能记得用这种函数啊
            if(res>=nums.size()-1){
                return true;
            }
        }
        return false;
    }
};

题目链接:45. 跳跃游戏 II - 力扣(Leetcode)

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){//遇到当前覆盖最远距离下标
                if(curDistance<nums.size()-1){//如果当前覆盖范围不能到达终点
                    ans++;//步数+1
                    curDistance=nextDistance;//更新当前最大覆盖范围
                    if(nextDistance>=nums.size()-1){//如果下一步最大覆盖范围能够达到终点,则不继续遍历
                        break;
                    }
                }
                else    break;//如果当前覆盖范围能够到达终点,则不继续遍历
            }
        }
        return ans;//返回步数
    }
};

题目链接:1005. K 次取反后最大化的数组和 - 力扣(Leetcode)

class Solution {
public:
    int largestSumAfterKNegations(vector<int>& nums, int k) {
        // 如果有负数,一定是负数变正数
        // 如果没有负数,则最小的变负
            // 当k为偶数时,可以将正数变负负数变正相当于没变,当k为奇数时才将正数变负,所以实际上只变一次
        sort(nums.begin(),nums.end());
        for(int i=0;i<nums.size();i++){//遍历数组将所有负数变成正数
            if(nums[i]<0&&k>0){
                nums[i]=-nums[i];
                k--;
            }
        }
        if(k%2!=0){// 当所有负数变成正数后,k仍>0且为奇数,则将最小正数变负
            sort(nums.begin(),nums.end());
            nums[0]=-nums[0];
        }
        int res=0;
        for(auto i:nums){
            res+=i;
        }
        return res;
    }
};

题目链接:134. 加油站 - 力扣(Leetcode)

// 方法一
// class Solution {
// public:
//     int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
//         int curSum=0;
//         int min=INT_MAX;//从起点出发,油箱里的油量最小值
//         for(int i=0;i<gas.size();i++){//从0出发遍历所有站点记录全部油量与所需油量的差值
//             int rest=gas[i]-cost[i];
//             curSum+=rest;
//             if(curSum<min){
//                 min=curSum;//min记录所有站点中某站点拥有的油量的所需油量的差值的最小值
//             }
//         }
//         if(curSum<0)    return -1;//如果全部油量不满足所需油量,返回-1表示不存在解
//         if(min>=0)      return 0;//如果途中每个站点都满足所需油量,返回起点0
//         // 如果全部油量满足所需油量但从0出发不满足,那么从最后一个站点出发,从后往前走,能把负数填平的站点就是出发站点
//         for(int i=gas.size()-1;i>=0;i--){
//             int rest=gas[i]-cost[i];
//             min+=rest;
//             if(min>=0){
//                 return i;
//             }
//         }
//         return -1;
//     }
// };

// 方法二
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){//如果当前剩余油量小于0,说明从之前的站点开始是行不通的
                start=i+1;//改变起始站点为下一个站点
                curSum=0;//当前剩余油量改为0,重新开始计算
            }
        }
        if(totalSum<0)  return -1;//如果所需全部油量大于所有油量,,则无解,返回-1
        return start;//如果有解则返回起始站点
    }
};

// // 我自己的想法
// // 其实跟方法二很像,只不过我思维太暴力了,不会剪枝
// class Solution {
// public:
//     int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
//         int gasLeft=0;//存储从起始站点到当前站点剩余油量
//         int station=0;//统计从起始站点到当前站点的站点数量
//         int start=0;//当前站点
//         int a=-1;//标记起始站点
//         int stationLeft=gas.size();//还剩多少站点没遍历过
//         while(station<gas.size()){//如果从起始站点到当前站点还不到一圈则继续循环
//             if(stationLeft>0)   stationLeft--;//如果还有站点没遍历过则-1
//             gasLeft+=gas[start]-cost[start];//记录剩余油量
//             // cout<<gasLeft<<endl;
//             if(gasLeft>=0){//如果剩余油量>=0说明前几站供需OK,可以继续往前走
//                 if(a==-1)   a=start;//如果起始站点未标记,则标记起始站点
//                 start=(start+1)%gas.size();//循环!记得是循环!
//                 station++;
//                 // cout<<start<<endl;
//             }
//             else{//如果供<需,则重新计算一圈
//                 gasLeft=0;//剩余油量归零
//                 station=0;//一圈重新开始,站点数量归零
//                 a=-1;//起始站点无
//                 start=(start+1)%gas.size();//往前走
//                 // cout<<start<<endl;
//             }
//             if(stationLeft==0&&a==-1){//当所有站点都遍历且起始站点还没找到时,无解
//                 // cout<<stationLeft<<endl;
//                 break;
//             }
//         }
//         return a;//返回起始站点
//     }
// };

题目链接:860. 柠檬水找零 - 力扣(Leetcode)

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){
                ten++;
                if(five>0)  five--;
                else    return false;
            }
            if(bill==20){
                twenty++;
                if(ten>0&&five>0){
                    ten--;
                    five--;
                }
                else if(five>=3){
                    five-=3;
                }
                else return false;
            }
        }
        return true;
    }
};

// class Solution {
// public:
//     bool lemonadeChange(vector<int>& bills) {
//         int curMoneySum=0;
//         int l=0;
//         map<int,int> m;
//         for(int i=0;i<bills.size();i++){
//             m[bills[i]]++;
//             curMoneySum+=5;
//             l=bills[i]-5;
//             cout<<curMoneySum<<endl;
//             if(curMoneySum<l){
//                 return false;
//             }
//             if(bills[i]>5&&curMoneySum>=l){
//                 if(m[5]==0){
//                     return false;
//                 }
//                 else if(l==5){
//                     m[5]--;
//                 }
//                 else if(l==15){
//                     if(m[10]>0){
//                         m[10]--;
//                         m[5]--;
//                     }
//                     else if(m[5]>=3){
//                         m[5]-=3;
//                     }
//                     else return false;
//                 }
//             }
//         }
//         return true;
//     }
// };

题目链接:406. 根据身高重建队列 - 力扣(Leetcode)

// vector
// 但使用vector是非常费时的,C++中vector(可以理解是一个动态数组,底层是普通数组实现的)如果插入元素大于预先普通数组大小,vector底部会有一个扩容的操作,即申请两倍于原先普通数组的大小,然后把数据拷贝到另一个更大的数组上。

// 所以使用vector(动态数组)来insert,是费时的,插入再拷贝的话,单纯一个插入的操作就是O(n^2)了,甚至可能拷贝好几次,就不止O(n^2)了。
class Solution {
public:
    vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
        // 题解说的方法我根本想不出来,他是怎么知道这样是最优解的?
        // 先根据身高排列,身高相同按照k的大小排列
        // 然后按照k插进去
        sort(people.begin(),people.end(),cmp);
        vector<vector<int>> que;
        for(int i=0;i<people.size();i++){
            int position=people[i][1];
            cout<<'['<<people[i][0]<<','<<people[i][1]<<"]-"<<position<<endl;
            // 这里是插入,就算原来的位子上已有元素也会插入进去
            que.insert(que.begin()+position,people[i]);
        }
        return que;
    }
    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];
    }
};

// 链表
// class Solution {
// public:
//     vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
//         // 先根据身高排列,身高相同按照k的大小排列
//         // 然后按照k插进去
//         sort(people.begin(),people.end(),cmp);
//         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());
//     }
//     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];
//     }
// };

题目链接:452. 用最少数量的箭引爆气球 - 力扣(Leetcode)

// // 我自己想的
// // 用一个数组记录当前最小的重合区域,在当前区域内就戳破,不在就把气球区域加入数组
// class Solution {
// public:
//     int findMinArrowShots(vector<vector<int>>& points) {
//         if(points.size()==0)    return 0;

//         int res=1;
//         sort(points.begin(),points.end(),cmp);
//         vector<vector<int>> minArea;
//         bool isPo=false;
//         for(int i=0;i<points.size();i++){
//             isPo=false;
//             for(int j=0;j<minArea.size();j++){
//                 // cout<<points[i][0]<<'-'<<minArea[j][1]<<endl;
//                 if(points[i][0]<=minArea[j][1]){
//                     isPo=true;
//                     minArea[j][0]=max(points[i][0],minArea[j][0]);
//                     minArea[j][1]=min(points[i][1],minArea[j][1]);
//                     break;
//                 }
//             }
//             // cout<<isPo<<endl;
//             if(!isPo){
//                 minArea.push_back(points[i]);
//             }
//         }
//         return minArea.size();
//     }
//         static bool cmp(const vector<int> &a,const vector<int> &b){
//         return a[0]<b[0];
//     }
// };

// 代码随想录
class Solution {
public:
    int findMinArrowShots(vector<vector<int>>& points) {
        if(points.size()==0)    return 0;
        
        int res=1;//如果points不为空则至少需要一支箭
        sort(points.begin(),points.end(),cmp);
        for(int i=1;i<points.size();i++){
            if(points[i][0]>points[i-1][1]){
                res++;
            }
            else{
                // 我本来还想整一个数组来记录最小重叠区域,但他是直接用上一个points[i]来记录,而且只记录右边界不记录左边界
                points[i][1]=min(points[i-1][1],points[i][1]);
            }
        }
        return res;
    }
    static bool cmp(const vector<int> &a,const vector<int> &b){
        return a[0]<b[0];
    }
};

题目链接:435. 无重叠区间 - 力扣(Leetcode)

// 对齐左边界
class Solution {
public:
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        if(intervals.size()==1) return 0;
        int res=0;
        sort(intervals.begin(),intervals.end(),cmp);
        int preEnd=intervals[0][1];
        for(int i=1;i<intervals.size();i++){
            if(intervals[i][0]<preEnd){
                res++;
                preEnd=min(preEnd,intervals[i][1]);//这里要取两者最小值,因为end越小与其他区间重叠的概率越低,需要移除的区间就越少
            }
            else{
                preEnd=intervals[i][1];
            }
            // cout<<preEnd<<endl;
        }
        return res;
    }
    static bool cmp(const vector<int> &a,const vector<int> &b){
        return a[0]<b[0];
    }
};

// 对齐右边界
// class Solution {
// public:
//     int eraseOverlapIntervals(vector<vector<int>>& intervals) {
//         if(intervals.size()==1) return 0;
//         int res=0;
//         sort(intervals.begin(),intervals.end(),cmp);
//         int count=1;
//         int end=intervals[0][1];
//         for(int i=0;i<intervals.size();i++){
//             if(end<=intervals[i][0]){//如果下一个区间不在当前最大右边界内,则扩大右边界,不重叠区间数+1
//                 end=intervals[i][1];
//                 count++;
//             }
//         }
//     }
//     static bool cmp(const vector<int> &a,const vector<int> &b){
//         return a[1]<b[1];
//     }
// };

题目链接:763. 划分字母区间 - 力扣(Leetcode)

class Solution {
public:
    vector<int> partitionLabels(string s) {
        // 我原来想的是记录每个字符的开始和结尾,这样就构成一个区间,然后就跟之前的区间问题同理了(本来想试着写一写但是写不出来orz)
        // 看题解之后:确实巧妙确实想不到
        int hash[27]={0};
        for(int i=0;i<s.size();i++){
            hash[s[i]-'a']=i;//统计每个字符最后出现的位置
        }
        vector<int> res;
        int left=0;//记录每个分割区间的左边界
        int right=0;//记录每个分割区间的右边界
        for(int i=0;i<s.size();i++){
            right=max(right,hash[s[i]-'a']);//更新当前区间中的最远位置
            if(i==right){//当到达当前区间的最远位置时,切割区间
                res.push_back(right-left+1);
                left=i+1;
            }
        }
        return res;
    }
};

题目链接:56. 合并区间 - 力扣(Leetcode)

不是代码随想录里的,做的上一题的相似题目

class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        vector<vector<int>> res;
        sort(intervals.begin(),intervals.end(),cmp);
        int end=intervals[0][1];
        int pos=0;
        for(int i=1;i<intervals.size();i++){
            if(intervals[i][0]<=end){
                end=max(end,intervals[i][1]);
                intervals[pos][1]=end;
            }
            else{
                res.push_back(intervals[pos]);
                // 这里要更新pos和end(一开始忘记更新end了)
                pos=i;
                end=intervals[pos][1];
            }
        }
        // 因为每一个区间都需要不重叠的下一个区间进行push,所以一定会漏掉一个区间没有push,所以最后push一下
        res.push_back(intervals[pos]);
        return res;
    }
    static bool cmp(const vector<int> &a,const vector<int> &b){
        return a[0]<b[0];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值