Leetcode 12月25日每日一题 切蛋糕的最小总开销 | 算法解题思路分享

前言

本博客会分享Leetcode3218. 切蛋糕的最小总开销 I解题思路,这是12月25日的每日一题。文章第二部分会分享一下自己的解题思维过程,如何通过刷题提升自己的算法能力。

3218. 切蛋糕的最小总开销 I - 力扣(LeetCode)

在这里插入图片描述

看完题目的第一反应,这道题应该是用贪心算法进行解题。可以想到满足最终答案res有以下公式:
r e s = ∑ C u t ∗ ( C o t O p p o s i t e S i d e + 1 ) res=\sum{Cut}*(CotOppositeSide+1) res=Cut(CotOppositeSide+1)
Cut指切蛋糕的花销,CotOppositeSide指异侧已经切了的刀数。根据样例中的动图:

  • 第一刀,沿垂直线0切时:Cut=5,CotOppositeSide=0
  • 第二刀,沿水平线0切时:Cut=1,CotOppositeSide=1
  • 以此类推…

在这里插入图片描述

对于某一刀花销Cut是一定的,所以CotOppositeSideres是正相关。要想使答案尽可能的小,就得尽量让较大的Cut对应较小的CotOppositeSide。结合题目中给的数据范围,一开始我想到用堆维护当前Cut的最大值,并且需要回溯去分别计算水平最大值优先或者垂直最大值优先的结果,最后再输出最小值。但是实际上并不需要,直接同时考虑全局下的花销最大值即可,也就有了如下的代码:

int minimumCost(int m, int n, vector<int>& horizontalCut, vector<int>& verticalCut) {
        priority_queue<pair<int,bool>,vector<pair<int,bool>>,less<>> q;
        for(int i=0;i<horizontalCut.size();i++){
            q.push({horizontalCut[i],true});
        }
        for(int i=0;i<verticalCut.size();i++){
            q.push({verticalCut[i],false});
        }

        int hori=0,vert=0;
        int res=0;
        while(!q.empty()){
            auto tmp=q.top();
            q.pop();
            if(tmp.second){
                res+=tmp.first*(vert+1);
                hori++;
            }
            else{
                res+=tmp.first*(hori+1);
                vert++;
            }
        }
        return res;
    }

这段代码能过这道题,甚至困难难度的3219. 切蛋糕的最小总开销 II - 力扣(LeetCode)也能勉强过。代码时间复杂度为O(mlogm+nlogn+(n+m)log(m+n)),仍有优化的空间

学习题解

优化自己的代码

如果只是单纯的维护Cut的最大值,没有必要使用堆去维护,每次出堆都会需要再次调整堆,导致了不必要的时间开销。只需要对数组进行排序,然后再用双指针控制此时Cut是最大值即可,这样处理之后算法的时间复杂度降低到了O(mlogm+nlogn).优化后得到了如下的代码:

int minimumCost(int m, int n, vector<int>& horizontalCut, vector<int>& verticalCut) {
        sort(horizontalCut.begin(),horizontalCut.end(),greater());
        sort(verticalCut.begin(),verticalCut.end(),greater());
        int hor=0,ver=0;
        auto it_hor=horizontalCut.begin(),it_ver=verticalCut.begin();
        int res=0;
        while(it_hor!=horizontalCut.end() || it_ver!=verticalCut.end()){
            //注意处理指针已经到数组最后的情况
            if(it_hor!=horizontalCut.end() && it_ver!=verticalCut.end()){
                if(*it_hor>(*it_ver)){
                    res+=(*it_hor)*(ver+1);
                    it_hor++;
                    hor++;
                }
                else{
                    res+=(*it_ver)*(hor+1);
                    it_ver++;
                    ver++;
                }
            }
            else if(it_hor!=horizontalCut.end()){
                res+=(*it_hor)*(ver+1);
                it_hor++;
            }
            else{
                res+=(*it_ver)*(hor+1);
                it_ver++;
            }
        }
        return res;
    }

学习新算法

在看题解的时候,关注都灵神的题解,用的是最小生成树的 Kruskal 算法。自然而然就会驱使自己去学习这个此前没有学习过的算法,加之有这道题的背景,学习新算法的时候就更容易理解新算法的思想,能更快掌握新的算法。至此,总结出如下的刷算法题的思维导图:

在这里插入图片描述

谢谢你的阅读!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值