当前位置: 首页 > article >正文

【代码随想录day29】【C++复健】134. 加油站;135. 分发糖果;860.柠檬水找零;406. 根据身高重建队列

感觉贪心的题真的很考验一个人的智商,而我,碰巧没有。

134. 加油站

偷看了解析思路,但依然因为一个低级失误导致被卡了很久... 并且写出来的代码也非常的麻烦,主要体现在我每到一个新节点,就必须求证似的让iter走完一整圈才算结束,但卡哥的方法一个for循环就结束了。

卡哥的思路也相当的清晰:

简单来说就是当你一圈下来:

1 如果和小于0,那么说明无论如何无解,也不用往后再看了。

2 如果和大于0,那么一圈之内必有解,假设从i开始不行,跳i+1,i+1假设后半段没问题,那再往后我也不管了,因为[0, i]如果没有的话,一定是在[i+1, n],又因为解唯一,假设某个位置能走到最后,后面的也不用看了。

我的这个代码就不用看了,写的太复杂了。

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        vector<int> plmi;
        for(int i=0; i<gas.size(); i++){
            plmi.push_back(gas[i]-cost[i]);
        }
        int sum = 0;
        for(int i=0; i<gas.size(); i++){
            sum += plmi[i];
        }
        if(sum < 0){
            return -1;
        }
        int count = 0;
        int start = 0;
        int iter = 0;
        int gas_remain = 0;
        while(count < gas.size()+1){
            gas_remain += plmi[iter];
            if(gas_remain < 0){
                count = 0;
                start = iter+1;
                gas_remain = 0;
                if(start >= gas.size()){
                    return -1;
                }
            }
            else{
                count++;
            }
            iter += 1;
            if(iter >= gas.size()){
                iter = 0;
            }
        }
        return start;
    }
};

135. 分发糖果

没有太多思路,就去看了解析,只能说看了解析之后接受的也有点勉强,主要感觉思维里面有几个比较重大的坎:


坎1:想到前后分着处理

这一步就足够难倒大多数英雄好汉了,感觉初见就是得被杀啊。所以我也是直接偷看了解析。

坎2:前后遍历完了,这两个数组怎么处理得到答案?

偷看了解析发现前后分着处理后,我直接把代码写出来了,但拿着这俩数组又不知道怎么办了。最后看卡哥的解析视频才终于把取max的合理性给看懂。简单来说就是左到右只满足右大于左时糖分的多,右到左只满足左大于右时糖分的多,两个取最大就是两边都满足了。

简单来说,就是大小关系在左到右和右到左的时候是错开的,不会出现两个数左到右遍历的时候要求右大于左,右到左遍历又要求左大于右,这种情况是不存在的。

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

860.柠檬水找零

思路和卡哥一样,也一遍做出来了,没什么太多好说的。

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

406.根据身高重建队列

关键问题1:分治先治哪边?

粗浅一看毫无思路,哪怕有了卡哥“类似分糖果,要先处理一边”的提示,还是想不出来,想到肯定是先处理h和k的其中一个,但是感觉给h排序没意义,给k排序更是毫无用处,就卡在这里了,感觉是自己想错了,就去偷看了解析。

然后卡哥表示:

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];
    }

没错,我确实分治了,但也没完全分,我在排h的时候顺便给k也排了,没想到吧!

 这样做的好处是极其明显的,因为如果不顺便排k的话,就会发现,你先插入的位置在后面还会发生变动,那就白排了。举个例子:[7,0], [7,1], [5,2], [5,0],假设我们先把前三个排好,后来发现[5,0]要排在[7,0]以前,那前面本来已经排好的[5,2]的位置就不对了,全部木大。

而如果顺便给k也排过的话,就可以保证[5,0]一定会在[5,2]之前出现,这样就能保证之前的积累,并非全部木大,未来的道路也会不断延伸...

关键问题2:要新建一个Vector!

冥思苦想了半天如果我要把原本在第i号位的元素挪到people[i][1]去的话应该怎么对数组进行操作,已经想到了说如果i比people[i][1]小的话,就从i+1开始,到people[i][1]为止,每个元素都往左挪动一格,然后再把i号位原本的元素挪到people[i][1]去,但后来一想,这还是个二维vector,赋值也是个超级大麻烦,索性直接去看解析了。一看恍然大悟,直接新建一个vector不就好了吗!

可能是受了之前那些原地操作的代码题的影响了,但真没必要,一切以俺代码写得舒服为最优先准则。

关键问题3:用vector还是用链表?

关于给vector补点插入值时会发生全量拷贝的事情也是有所耳闻的,也知道链表会更好,但奈何...

链表的定义都忘记了你敢信!用的是什么关键词来着?

好吧,原来就是list,写成list<vector<int>>,并且为了遍历我们还需要再定义一个迭代器,迭代器内置了++操作,每次position--,it就++,直到potision减到0。

        list<vector<int>> que; // list底层是链表实现,插入效率比vector高的多
        for (int i = 0; i < people.size(); i++) {
            int position = people[i][1]; // 插入到下标为position的位置
            std::list<vector<int>>::iterator it = que.begin();
            while (position--) { // 寻找在插入位置
                it++;
            }
            que.insert(it, people[i]);
        }

只能说全是细节,我估计是写不出来的,更何况最后还有一个转换:

return vector<vector<int>>(que.begin(), que.end());

怎么说呢,都是一看就懂,一写就废,根本不知道要传什么参,怎么写。 


http://www.kler.cn/a/392632.html

相关文章:

  • 2024/11/13 英语每日一段
  • 十三、注解配置SpringMVC
  • 区块链技术在慈善捐赠中的应用
  • aws(学习笔记第十二课) 使用AWS的RDS-MySQL
  • Unity资源打包Addressable资源保存在项目中
  • 企业如何提高团队管理的能力?
  • [动态规划]最长公共子序列
  • vue 计算属性get set
  • 白酒除高级醇提升口感工艺
  • Javascript高级—如何实现一个类型判断函数?
  • 基于复现油炸鸡的智能手表的过程(1)
  • windows工具 -- 使用rustdesk和云服务器自建远程桌面服务, 手机, PC, Mac, Linux远程桌面 (简洁明了)
  • 前端-同源与跨域
  • 【解决】Layout 下创建槽位后,执行 Image 同步槽位位置后表现错误的问题。
  • 为什么RNN(循环神经网络)存在梯度消失和梯度爆炸?
  • 自动驾驶系列—自动驾驶车辆的姿态与定位:IMU数据在复杂环境中的关键作用
  • Python PyQt5 实现 .his 文件批量转 Excel 工具
  • 代码版本管理艺术
  • 【Python TensorFlow】进阶指南(续篇一)
  • SpringCloud学习笔记
  • LeetCode【0021】合并两个有序链表
  • HBuilder(uniapp) 配置android模拟器
  • php回调函数(匿名)的使用
  • IC 脚本之VIM 记录
  • 构建高效在线商店:Spring Boot框架应用
  • three.js 杂记