代碼隨想錄算法訓(xùn)練營打卡Day34 | LeetCode1005 K次取反后最大化的數(shù)組和、eetCode134 加油站堪旧、LeetCode135 分發(fā)糖果

摘要

  • 貪心法的思考一般都貼合常識(shí)削葱,比較直觀,但是要寫出邏輯正確的代碼并不簡(jiǎn)單淳梦。
  • 如何將全局最優(yōu)分解為幾步容易求解的局部最優(yōu)并最終“合成”全局最優(yōu)析砸,是一個(gè)思考的方向。

LeetCode1005 K次取反后最大化的數(shù)組和

1005. K 次取反后最大化的數(shù)組和 - 力扣(Leetcode)

  • 貪心法一般都貼合常識(shí)爆袍,比較直觀首繁,要取反后讓數(shù)組和最大作郭,首先要考慮每一步如何取反能讓數(shù)組和增大:
    • 對(duì)一個(gè)負(fù)數(shù)取反,數(shù)組和肯定會(huì)增大弦疮,對(duì)絕對(duì)值越大的負(fù)數(shù)取反夹攒,數(shù)組和越大。
    • 對(duì)0取反胁塞,數(shù)組和不變
    • 對(duì)一個(gè)正數(shù)取反咏尝,數(shù)組和會(huì)變小,如果一定要對(duì)正數(shù)取反啸罢,應(yīng)該絕對(duì)值小的正數(shù)取反
  • 以上每種情況都和絕對(duì)值有關(guān)编检,所以首先想到將數(shù)組按絕對(duì)值從大到小排列,然后從左向右遍歷一次數(shù)組:
    • 遇到一個(gè)負(fù)數(shù)伺糠,則取反蒙谓,然后k--
    • 遇到0或正數(shù)不進(jìn)行操作
    • 遍歷的同時(shí)計(jì)算數(shù)組的和
  • 遍歷完一次數(shù)組后,如果k > 0训桶,則不斷對(duì)數(shù)組最后一個(gè)元素(即絕對(duì)值最小的元素)取反直到k == 0累驮。

完整的題解代碼如下

class Solution {
public:
    static bool cmp(int a, int b) {
        return abs(a) > abs(b);
    }
    int largestSumAfterKNegations(vector<int>& nums, int k) {
        sort(nums.begin(), nums.end(), cmp);
        
        int sum = 0;
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] < 0 && k > 0) {
                nums[i] *= -1;
                k--;
            }
            sum += nums[i];
        }
        
        while (k) {
            nums[nums.size()- 1] *= -1;
            sum += 2 * nums[nums.size()- 1];
            k--;
        }
        return sum;
    }
};

LeetCode134 加油站

134. 加油站 - 力扣(Leetcode)

  • 從左到右遍歷數(shù)組,首先假設(shè)以i為起點(diǎn)午绳,

    • 計(jì)算汽車從i號(hào)加油站出發(fā)到i+1號(hào)加油站后剩余的燃油curGas置侍。
    • 如果汽車剩余燃油值curGas小于0,說明從i不是一個(gè)合理的起點(diǎn)拦焚,假設(shè)i+1為起點(diǎn)蜡坊,將curGas歸零,繼續(xù)計(jì)算每段行程后的curGas赎败。
    • 另外秕衙,計(jì)算汽車行駛完整個(gè)環(huán)形路徑后的剩余燃油totalGas,作為判斷最后的起點(diǎn)坐標(biāo)是否合理的依據(jù)僵刮。如果totalGas小于0据忘,說明無論從哪里開始,都不能走完整個(gè)環(huán)形路徑搞糕,因?yàn)槠囈婚_始的燃油量是0勇吊,并沒有額外的燃油。

完整的題解代碼如下

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int start = 0;
        int totalGas = 0;
        int curGas = 0;
        for (int i = 0; i < gas.size(); i++) {
            totalGas += gas[i] - cost[i];
            curGas += gas[i] - cost[i];
            if (curGas < 0) {
                curGas = 0;
                start = i + 1;
            }
        }
        return totalGas >= 0 ? start : -1;
    }
};
  • 直觀地來理解:totalGas的值告訴我們是否有解窍仰,即是否存在一個(gè)加油站作為起點(diǎn)使得汽車能走完環(huán)形路徑汉规;而curGas的值告訴我們start是否是一個(gè)合理的起點(diǎn),如果start不是一個(gè)合理的起點(diǎn)驹吮,那么從start出發(fā)后經(jīng)過的加油站組成的也不是一個(gè)合理的路徑针史。所以我們直接認(rèn)為從starti號(hào)的加油站都不是合理的起點(diǎn)膏燕,繼續(xù)假設(shè)i+1是一個(gè)合理的起點(diǎn)。

LeetCode135 分發(fā)糖果

135. 分發(fā)糖果 - 力扣(Leetcode)

  • 初見題目的想法:先從左到右遍歷一次ratings數(shù)組悟民,保證在兩個(gè)相鄰的孩子中,如果左邊的孩子的ratings較大篷就,那么他得到的糖果就會(huì)比右邊的孩子多射亏。再從右到左遍歷一次ratings數(shù)組,保證在兩個(gè)相鄰的孩子中竭业,如果右邊的孩子的ratings較大智润,那么他得到的糖果就會(huì)比左邊的孩子多。

初見題目的代碼未辆,思路還不夠清晰窟绷,最后一個(gè)用例超時(shí)了。

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

[圖片上傳失敗...(image-b728c6-1683603409915)]

  • 看過講解后咐柜,這道題的貪心解法確實(shí)很巧妙兼蜈,通過兩步的局部最優(yōu)得到了全局最優(yōu)。
    • 相鄰兩個(gè)孩子評(píng)分更高的孩子會(huì)獲得更多糖果拙友,這描述了全局最優(yōu)为狸。
    • 當(dāng)ratings[i - 1] < ratings[i]時(shí),孩子i比孩子i-1得到的糖果更多遗契。按照這個(gè)規(guī)則從左到右遍歷一次ratings辐棒,如果兩個(gè)孩子相鄰且在右邊的孩子(即孩子i)的ratings較大,則孩子i獲得的糖果要比在左邊的孩子(即孩子i-1)多牍蜂。
      • 這保證了對(duì)于每個(gè)孩子i漾根,如果在他的ratings比左邊的孩子i-1大,則孩子i獲得的糖果更多鲫竞。這只保證了“每個(gè)孩子比左邊評(píng)分較低的孩子獲得的糖果更多”辐怕,是一步局部最優(yōu)。
    • 同樣的贡茅,當(dāng)ratings[i] > ratings[i + 1]時(shí)秘蛇,孩子i比孩子i+1得到的糖果更多。按照這個(gè)規(guī)則從右到左遍歷一次ratings顶考,如果兩個(gè)孩子相鄰且在左邊的孩子(即孩子i)的ratings較大赁还,則孩子i獲得的糖果要比在右邊的孩子(即孩子i+1)多。
      • 這保證了對(duì)于每個(gè)孩子i驹沿,如果在他的ratings比右邊的孩子i+1大艘策,則孩子i獲得的糖果更多。這只保證了“每個(gè)孩子比右邊評(píng)分較低的孩子獲得的糖果更多”渊季,是一步局部最優(yōu)朋蔫。
    • 對(duì)每個(gè)孩子罚渐,取以上兩步中計(jì)算出的較大的糖果數(shù),就能保證每個(gè)孩子既比左邊評(píng)分較低的孩子獲得的糖果更多驯妄,又比右邊評(píng)分較低的孩子獲得的糖果更多荷并。
  • 如何通過局部最優(yōu)來得到全局最優(yōu)?或許可以嘗試思考把全局最優(yōu)的規(guī)則分為局部最優(yōu)的規(guī)則青扔。這道題說難也難源织,說簡(jiǎn)單也簡(jiǎn)單,畢竟說白了就是先保證每個(gè)孩子比左邊評(píng)分較低的孩子多微猖,再保證每個(gè)孩子比右邊評(píng)分較低的孩子多谈息。將全局最優(yōu)(相鄰的孩子)分解為兩個(gè)局部最優(yōu)——比左邊評(píng)分較低的多、比右邊評(píng)分較低的多凛剥。如何把全局最優(yōu)分解成容易實(shí)現(xiàn)的局部最優(yōu)或許也是一個(gè)思考的方向侠仇。

題解代碼如下

class Solution {
public:
    int candy(vector<int>& ratings) {
        vector<int> candies(ratings.size(), 1);

        for (int i = 1; i < ratings.size(); i++) {
            if (ratings[i - 1] < ratings[i]) 
                candies[i] = candies[i - 1] + 1;
        }

        for (int i = ratings.size() - 2; i >= 0; i--) {
            if (ratings[i] > ratings[i + 1]) 
                candies[i] = max(candies[i + 1] + 1, candies[i]);
        }

        int res = 0;
        for (auto& iter : candies) {
            res += iter;
        }
        return res;
    }
};
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市犁珠,隨后出現(xiàn)的幾起案子逻炊,更是在濱河造成了極大的恐慌,老刑警劉巖犁享,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嗅骄,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡饼疙,警方通過查閱死者的電腦和手機(jī)溺森,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門梯澜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來意狠,“玉大人砚作,你說我怎么就攤上這事绢记√母” “怎么了假残?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵绩社,是天一觀的道長筹我。 經(jīng)常有香客問我卷要,道長渣聚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任僧叉,我火速辦了婚禮奕枝,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瓶堕。我一直安慰自己隘道,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著谭梗,像睡著了一般忘晤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上激捏,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天设塔,我揣著相機(jī)與錄音,去河邊找鬼远舅。 笑死壹置,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的表谊。 我是一名探鬼主播,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼盖喷,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼爆办!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起课梳,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤距辆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后暮刃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體跨算,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年椭懊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了诸蚕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡氧猬,死狀恐怖背犯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情盅抚,我是刑警寧澤漠魏,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站妄均,受9級(jí)特大地震影響柱锹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜丰包,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一禁熏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧邑彪,春花似錦匹层、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽撑柔。三九已至,卻和暖如春您访,著一層夾襖步出監(jiān)牢的瞬間铅忿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來泰國打工灵汪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留檀训,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓享言,卻偏偏與公主長得像峻凫,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子览露,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容