C++刷题笔记(33)——leetcode1005、134、135、860、406

题目1:1005.K次取反后最大化的数组和

C++刷题笔记(33)——leetcode1005、134、135、860、406_第1张图片
这一题思路还是比较容易想出来的,从数组中绝对值大的负数开始变,直到k=0;如果k没用完就变数组中最小点数

class Solution {
public:
    static bool cmp(int a, int b) {
        return abs(a) > abs(b);                 //abs求整型数据的绝对值
    }
    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) {           //遇到负数就将其变为正数、k-1
                nums[i] = -nums[i];
                k--;
            }
        }
        while (k > 0) {
            nums[nums.size() - 1] *= -1;       //遍历完数组k仍大于0,那么翻转数值最小的元素直到k为0
            k--;
        }
        //if(k %2 ==1) nums[nums.size() - 1] *= -1;  k取模2余1(k为奇数),相当于要对数值最小的元素进行奇数次修改;如果k取模2余0,相当于对一个数进行偶数次修改,等价于不修改                                      
        int result = 0;
        for (int a : nums) result += a;        //求和
        return result;
    }
};

题目2:134. 加油站

C++刷题笔记(33)——leetcode1005、134、135、860、406_第2张图片
暴力解法好,但是这一题的暴力解法不好写:

从头开始遍历每个加油站,看最后能够返回。
for循环适合模拟从头到尾的遍历,而while循环适合模拟环形遍历

class Solution {
public:
    int canCompleteCirecuit(vector<int>& gas, vector<int>& cost) {
        for (int i = 0; i < cost.size(); i++) {     //遍历加油站,以每一个加油站为起点
            int rest = gas[i] - cost[i];            //剩余油量
            int next = (i + 1) % cost.size();      //(i+1)
            while (rest > 0 && next != i) {        //以i加油站为起点行驶一圈
                rest += gas[next] - cost[next];   //累计剩余油量
                next = (next + 1) % cost.size();  //更新下一站位置
            }
            if (rest >= 0 && next == i) return i;  //行驶一圈后剩余油量>=0说明能环绕一周,返回加油站编号
        }
        return -1;
    }
};

暴力解法的缺点就是进行了很多重复计算

如果 总油量减去总消耗大于零,那么就一定存在一个加油站可以跑完一圈;

此外,如果某个区间的剩余总油量小于零,那肯定起点肯定不会在这个区间
C++刷题笔记(33)——leetcode1005、134、135、860、406_第3张图片

class Solution {
public:
    int canCompleteCirecuit(vector<int>& gas, vector<int>& cost) {
        int currest = 0;
        int totalrest = 0;                  //剩余总油量
        int start = 0;                      //开始位置
        for (int i = 0; i < gas.size(); i++) {
            currest += gas[i] - cost[i];      
            totalrest += gas[i] - cost[i];
            if (currest < 0) {              //[0,i]的总油量小于零,那肯定起点肯定不会在这个区间
                start = i + 1;              //更新起点
                currest = 0;                //当前区间总油量
            }
        }
        if (totalrest < 0) return -1;       //剩余总油量小于0,那么从哪都不能行驶一圈
        return start;
    }
};

题目3:135. 分发糖果

C++刷题笔记(33)——leetcode1005、134、135、860、406_第4张图片
困难题都是谜语人,把规则理解清楚就好做了

相邻的孩子中,评分高的孩子必须获得更多的糖果 可以拆分成:

左规则:当ratings[i-1] < ratings[i]时,i号学生的糖果数比i-1号孩子的糖果数多。
右规则:当ratings[i]>ratings[i+1]时,i号学生的糖果数比i+1号孩子的糖果数多。

在相邻的学生中,评分高的孩子必须获得更多的糖果等价于 必须同时满足左右规则
C++刷题笔记(33)——leetcode1005、134、135、860、406_第5张图片
局部最优:取candyvec[i + 1] + 1 和 candyvec[i] 最大的糖果数量,保证第i个小孩的糖果数量即大于左边的也大于右边的;
全局最优:相邻的孩子中,评分高的孩子获得更多的糖果

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 < ratings.size(); i++) result += candyvec[i];
        return result;
    }
};

题目4:860.柠檬水找零

C++刷题笔记(33)——leetcode1005、134、135、860、406_第6张图片
一杯柠檬水五刀?他们没有支付宝的吗?

收钱时有三种情况:(1)顾客支付五美元,直接收下;(2)顾客支付十美元,找五美元;(3)顾客支付二十美元,找一张五美元和十美元、或找三张五美元

这里有用到贪心,就是支付二十美元时,优先找零一张五美元和一张十美元


class Solution {
public:
    bool lemonadeChange(vector<int>& bills) {
        int five = 0, ten = 0, twenty = 0;
        //for (int i = 0; i < bills.size(); i++) 用bills[i]判断
        for (int bill : bills) {
            if (bill == 5) five++;             //五美元,直接收下
            if (bill == 10) {                  //十美元
                if (five <= 0) return false;   //如果没有五美元就寄
                ten++;                         //如果有五美元就找零
                five--;
            }
            if (bill == 20) {                  //二十美元
                if (five > 0 && ten > 0) {     //有十美元和五美元就找零
                    five--;
                    ten--;
                    twenty++;
                }
                else if (five >= 3) {          //没有十美元的话如果有三张五美元也行
                    five -= 3;
                    twenty++;
                }
                else return false;
            }
        }
        return true;
    }
};

题目5:406.根据身高重建队列

C++刷题笔记(33)——leetcode1005、134、135、860、406_第7张图片
和135题类似,同样是有两个规则:
规则1:按身高h从高到低排序,身高相同则k小的在前面
规则2:排序后按k为下标重新插入队列
C++刷题笔记(33)——leetcode1005、134、135、860、406_第8张图片

class Solution {
public:
    static bool cmp(const vector<int>& a, const vector<int>& b) {        //身高从大到小排(身高相同k小的站前面)
        if (a[0] == b[0]) return a[1] < b[1];
        return a[0] > b[0];
    }
    vector<vector<int>> reconstructQurue(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];                         //插入到下标为position的位置
            que.insert(que.begin() + position, people[i]);
        }
        return que;
    }
};

你可能感兴趣的:(不知道能不能混口饭的C嘎嘎,c++,leetcode,算法,数据结构,贪心算法)