代码随想录算法训练营第二十六天| 134. 加油站, 135. 分发糖果 , 860.柠檬水找零, 406.根据身高重建队列

今天是贪心算法学习的第三天,感觉有摸到一点门道。贪心算法感觉还是离不开对题目的模拟。

134. 加油站

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

该题目给出了两个数组,一个是每个加油站能加的油量,一个是到达该加油站所消耗的油量。需要从数组中挑选一个起点,从该起点出发可以遍历完这个环形路段。随想录提供了两个题解,我觉得第二种思路是更好理解的,两种解题思路都写一下。

一种思路是首先求解一个数组,对于每一个加油站求解一个净加油量,就是用加油量减去路上损耗的油量,并对净加油量求和,如果求和为负说明无法实现走完整个数组。如果求和量为正,就从0开始遍历净加油量数组并求和,一旦求和为负,就将求和量置为0,并从下一个点为新的起点。具体代码实现如下:
 

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        vector<int> nums(gas.size(),0);
        for(int i=0;i<nums.size();i++) nums[i]=gas[i]-cost[i];
        int sum=0;
        for(int i=0;i<nums.size();i++) sum+=nums[i];
         int start=0;
        if(sum<0) return -1;
        else
        {
           int rest_sum=0;
           for(int i=start;i<nums.size();i++)
           {
              rest_sum+=nums[i];
              if(rest_sum<0) 
              {
                start=i+1;
                rest_sum=0;
                continue;
              }
           }
        }
        return start;
    }
};

第一种思想我个人是不太好理解的,我觉得第二种思路更加好理解。同样是对净加油量数组,从头开始求和,求解一个求和的最小值,假如一个数组的和是大于0的,要不两端子数组都大于0,要不一段大于0一段小于0;如果所有子数组都是大于0,那么求和的最小值就是大于0的,此时直接选择0作为起点即可;如果是一段大于0一段小于0,我们最小的求和子数组,从他后面开始求和一定是大于0的。具体代码实现如下:
 

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        vector<int> nums(gas.size(),0);
        for(int i=0;i<nums.size();i++) nums[i]=gas[i]-cost[i];
        int sum=0;
        for(int i=0;i<nums.size();i++) sum+=nums[i];
         int min_sum=10010;
        int index=0;
        int msum=0;
        if(sum<0) return -1;
        else
        {
           for(int i=0;i<nums.size();i++)
           {
             msum+=nums[i];
             if(min_sum>msum)
             {
                index=i;
                min_sum=msum;
             }
           } 
        }
        if(min_sum>0) return 0;
        if(index==nums.size()-1) return 0;
        else return index+1;
    }
};

135. 分发糖果

题目链接:135. 分发糖果 - 力扣(LeetCode)

这个题目是给出一个评分数组,要求相邻孩子中较高评分的孩子有更多的糖果数,求满足这一条件的最小糖果总和。

思路是这样的,首先从左往右遍历评分数组,如果右孩子大于左孩子,并且右孩子的糖果小于等于左孩子的糖果,就把右孩子的糖果在左孩子糖果的基础上加1;再从右往左遍历评分数组,如果左孩子大于右孩子,并且左孩子的糖果小于等于右孩子的糖果,就把左孩子的糖果在右孩子糖果的基础上加1。这样两次贪心的过程就可以得到全局最优解。具体代码实现如下:
 

class Solution {
public:
    int candy(vector<int>& ratings) {
        vector<int> candy(ratings.size(),1);
        for(int i=0;i<candy.size()-1;i++)
        {
            if(ratings[i+1]>ratings[i]&&candy[i+1]<=candy[i]) candy[i+1]=candy[i]+1;
        }
        for(int i=candy.size()-1;i>0;i--)
        {
            if(ratings[i-1]>ratings[i]&&candy[i-1]<=candy[i]) candy[i-1]=candy[i]+1;
        }
        int sum=0;
        for(int i=0;i<candy.size();i++) sum+=candy[i];
        return sum;

    }
};

860.柠檬水找零

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

这个题目挺简单的,就是一个模拟题。用一个数组统计5元,10元,20元纸币的数量,收到一张十元就退一张五元,收到一张二十可以退一五一十或三张五元,优先选择一五一十的退法,因为五月是更有用的。最后判断是否有某个纸币为负值,就可以判断是否可行。具体代码实现如下:
 

class Solution {
public:
    bool lemonadeChange(vector<int>& bills) {
        int nums[3]={0};
        for(int i=0;i<bills.size();i++) 
        {
            if(bills[i]==5) nums[0]++;
            else if(bills[i]==10) 
            {
                nums[0]--;
                nums[1]++;
            }
            else if(bills[i]==20)
            {
                if(nums[1]>0)
                {
                    nums[0]--;
                    nums[1]--;
                    nums[2]++;
                }
                else{
                    nums[0]-=3;
                }
            }
            for(int i=0;i<3;i++) if(nums[i]<0) return false;
        }
        return true;
    }
};

406.根据身高重建队列

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

这个题目有点难度。题目的要求是给出一个数组,里面每个元素包含两个属性h和k,h是身高,k是前面有k个人的身高大于等于h。要求将数组正确的排序。

思路是这样的。首先将数组由身高从大到小排序,身高一样的按k从小到大排序。然后遍历整个数组,将元素插入到新数组的k的位置,由于是身高从大到小插入,即使有新的元素插入到前面也不影响后面的元素的k的位置,因为新的元素的身高一定小于等于已经在数组中的元素。

还有一个值得注意的点,这个题使用list容器比使用vector容器时间复杂度会优很多。这是由于list的底层实现是链表,插入元素是o(1)的时间复杂度,vector的底层实现是数组,插入元素要将后面的所有元素右移,扩容时是直接复制一个同等大小的空数组并进行拼接。所以插入操作的效率会比list低很多。该题目的具体代码实现如下所示:
 

class Solution {
public:
static bool cmp(vector<int> &a,vector<int> &b)
{
    if(a[0]!=b[0]) return a[0]>b[0];
    else return a[1]<b[1];
}
    vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
        list<vector<int>> que;
        sort(people.begin(),people.end(),cmp);
        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());
    }
};

  • 23
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值