代码随想录算法训练营第34天 | 贪心算法 part03● 1005.K次取反后最大化的数组和 ● 134. 加油站● 135. 分发糖果

day 33 是休息日

#1005 K次取反后最大化的数组和, easy 5min 

其实做有些贪心的题完全没想着贪心,完全按常识做的。注意:把负数和0反转完之后,剩的k是偶数就完全不用管了(转两次等于没做),是奇数的话就把k当还剩1次就行

int largestSumAfterKNegations(vector<int>& nums, int k) {
        sort(nums.begin(),nums.end());
        for(int i=0;i<nums.size() && nums[i]<=0 && k>0;i++){
            k--;
            nums[i]*=-1;
        }
        
        sort(nums.begin(),nums.end());
        if(k>0 && k%2!=0) nums[0]=-1*nums[0];
            
        int sum=0;
        for(int &ele:nums) sum+=ele;
        return sum;
    }

随想录思路有点不同,但我感觉这题不重要,没看。先粘上来:

static bool cmp(int a, int b) {
    return abs(a) > abs(b);
}
public:
    int largestSumAfterKNegations(vector<int>& A, int K) {
        sort(A.begin(), A.end(), cmp);       // 第一步
        for (int i = 0; i < A.size(); i++) { // 第二步
            if (A[i] < 0 && K > 0) {
                A[i] *= -1;
                K--;
            }
        }
        if (K % 2 == 1) A[A.size() - 1] *= -1; // 第三步
        int result = 0;
        for (int a : A) result += a;        // 第四步
        return result;
    }

#134 加油站 M

我服了我自己,这道题总共花了两个多小时。几个原因吧:我非觉得我的思路没问题(是仿照前面一道题 算累加和如果为负数,就放弃前面的,从下一个当新起点开始),这个思路确实大体是对的,但是还需要再巧妙一点。另外就是我觉得确实是leetcode编译器出了点问题,但是后来又莫名其妙好了

下面的截图记录一下发生的奇怪事情:

 “那你觉得我的逻辑对吗” “ 你的代码逻辑基本上是正确的。你在循环中从每个站点开始尝试,每次都尝试到下一个站点,如果油量不足,则从下一个站点重新开始” 

总之,学到的是,当出现非常反常,难以理解跑出来的效果时,可以合理怀疑是编译器的问题,可以换一个编译器比如online c++ compiler 试一试,或者刷新一下页面,等一会再尝试,不要连续猛烈地死磕。

另外还想说,我觉得自己有点太固执了,每次都想证明自己的思路是行得通的,于是花非常多时间去调整修改code。做为1刷真的非常没有必要,浪费很多时间,这一点一定要改掉。

----- ------- ------ ----- ------ ----- 分割线:下面正经讲这道题 ------ ------ ------ ----- ----- ------ ----- ----- --

我的思路确实大体正确,但实现不够巧妙,而且写出来代码看一看就会发现是worst case 是O n^2 ,这和直接暴力两层for loop效率差不多( 两层这个过不了 TLE)。下面是我的没通过的代码和没通过的case:

int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int sum=0;
        int cnt=0; 
        int i=0;
        bool finish=false;

        while(cnt<gas.size()){
            cout<<" start !!!!! sum :"<<sum<<", cnt: "<<cnt<<",gas size: "<<gas.size()<<endl;
            //i=i%gas.size();
            cout<<"check "<<i<<endl;
            sum+=gas[i]-cost[i];
            cnt++;
            if(sum<0){
                cout<<"met nega, start over, sum= "<<sum<<endl;
                cout<<"sum :"<<sum<<", cnt: "<<cnt<<",gas size: "<<gas.size()<<endl;
                if(finish) {
                    cnt=0;
                    break;
                }
                cnt=0;
                sum=0;
            }
            i++;
            if(i==gas.size()) {
                i=0;
                finish=true;
            }
        }
        if(cnt==0) return -1;
        return i%gas.size();
    }

 下面是随想录思路思路自己又写了一遍code,巧妙之处是一直维持了一个totalsum,一直都可以用,比如我们走到倒数第二个发现不行,准备从倒数第一个再开始,这时不用重新计算,因为这是最后一次机会了,看total如果再<0,就根本走不完一圈了

你看这清爽的逻辑和代码,本来是30min就能ac+博客全弄完的

int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        
        int totalsum=0;
        int cursum=0;
        int start=0;

        for(int i=0;i<gas.size();i++){
            totalsum+=gas[i]-cost[i];
            cursum+=gas[i]-cost[i];
            if(cursum<0){
                start=i+1;
                cursum=0;
            }
        }
        if(totalsum<0) return -1;
        return start;
    }

随想录还有另外一种巧妙的方法,我还没太理解,先放在这:

int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int curSum = 0;
        int min = INT_MAX; // 从起点出发,油箱里的油量最小值
        for (int i = 0; i < gas.size(); i++) {
            int rest = gas[i] - cost[i];
            curSum += rest;
            if (curSum < min) {
                min = curSum;
            }
        }
        if (curSum < 0) return -1;  // 情况1
        if (min >= 0) return 0;     // 情况2
                                    // 情况3
        for (int i = gas.size() - 1; i >= 0; i--) {
            int rest = gas[i] - cost[i];
            min += rest;
            if (min >= 0) {
                return i;
            }
        }
        return -1;
    }

#135 分发糖果 Hard, 关键:如果在考虑局部的时候想两边兼顾,就会顾此失彼

自己没想出来。随想录思路:

1.每个和左边的/前面的邻居比

从左向右:我比前面大吗,大就=前面+1

2.每个和右边/后面的邻居比

从右向左:我比后面大吗,大就!!!取max(后面+1,已经更新过的前面+1)

就是比左右邻居都大的话,要选择左右邻居里面数值大的那个+1

这里一定要从后往前遍历!因为既然是和后面的比,在后一个元素值基础上更新,那就要把后面的值先算出来

 vector初始化要记得会写:vector<int> vec(size, value );

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 res = 0;
        for (auto &ele:candyVec) res+=ele;
        return res;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值