貪心算法:加油站

2021-02-21 代碼隨想錄

通知:一些錄友基礎比較薄弱,不知道從哪裡開始刷題。可以看一下公眾號左下角的「算法匯總」,「算法匯總」已經把題目順序編排好了,文章順序即刷題順序,這是全網最詳細的刷題順序了,方便錄友們從頭打卡學習,「算法匯總」會持續更新!

❞134. 加油站

題目連結:https://leetcode-cn.com/problems/gas-station/

在一條環路上有 N 個加油站,其中第 i 個加油站有汽油 gas[i] 升。

你有一輛油箱容量無限的的汽車,從第 i 個加油站開往第 i+1 個加油站需要消耗汽油 cost[i] 升。你從其中的一個加油站出發,開始時油箱為空。

如果你可以繞環路行駛一周,則返回出發時加油站的編號,否則返回 -1。

說明:

示例 1:
輸入:
gas  = [1,2,3,4,5]
cost = [3,4,5,1,2]

輸出: 3
解釋:
從 3 號加油站(索引為 3 處)出發,可獲得 4 升汽油。此時油箱有 = 0 + 4 = 4 升汽油
開往 4 號加油站,此時油箱有 4 - 1 + 5 = 8 升汽油
開往 0 號加油站,此時油箱有 8 - 2 + 1 = 7 升汽油
開往 1 號加油站,此時油箱有 7 - 3 + 2 = 6 升汽油
開往 2 號加油站,此時油箱有 6 - 4 + 3 = 5 升汽油
開往 3 號加油站,你需要消耗 5 升汽油,正好足夠你返回到 3 號加油站。
因此,3 可為起始索引。

示例 2:
輸入:
gas  = [2,3,4]
cost = [3,4,3]

輸出: -1
解釋:
你不能從 0 號或 1 號加油站出發,因為沒有足夠的汽油可以讓你行駛到下一個加油站。
我們從 2 號加油站出發,可以獲得 4 升汽油。此時油箱有 = 0 + 4 = 4 升汽油
開往 0 號加油站,此時油箱有 4 - 3 + 2 = 3 升汽油
開往 1 號加油站,此時油箱有 3 - 3 + 3 = 3 升汽油
你無法返回 2 號加油站,因為返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。
因此,無論怎樣,你都不可能繞環路行駛一周。

思路暴力方法

暴力的方法很明顯就是O(n^2)的,遍歷每一個加油站為起點的情況,模擬一圈。

如果跑了一圈,中途沒有斷油,而且最後油量大於等於0,說明這個起點是ok的。

暴力的方法思路比較簡單,但代碼寫起來也不是很容易,關鍵是要模擬跑一圈的過程。

「for循環適合模擬從頭到尾的遍歷,而while循環適合模擬環形遍歷,要善於使用while!」

C++代碼如下:

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        for (int i = 0; i < cost.size(); i++) {
            int rest = gas[i] - cost[i]; // 記錄剩餘油量
            int index = (i + 1) % cost.size();
            while (rest > 0 && index != i) { // 模擬以i為起點行駛一圈
                rest += gas[index] - cost[index];
                index = (index + 1) % cost.size();
            }
            // 如果以i為起點跑一圈,剩餘油量>=0,返回該起始位置
            if (rest >= 0 && index == i) return i;
        }
        return -1;
    }
};

C++暴力解法在leetcode上提交也可以過。

貪心算法(方法一)

直接從全局進行貪心選擇,情況如下:

情況一:如果gas的總和小於cost總和,那麼無論從哪裡出發,一定是跑不了一圈的

情況二:rest[i] = gas[i]-cost[i]為一天剩下的油,i從0開始計算累加到最後一站,如果累加沒有出現負數,說明從0出發,油就沒有斷過,那麼0就是起點。

情況三:如果累加的最小值是負數,汽車就要從非0節點出發,從後向前,看哪個節點能這個負數填平,能把這個負數填平的節點就是出發節點。

C++代碼如下:

class Solution {
public:
    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;
    }
};

「其實我不認為這種方式是貪心算法,因為沒有找出局部最優,而是直接從全局最優的角度上思考問題」

但這種解法又說不出是什麼方法,這就是一個從全局角度選取最優解的模擬操作。

所以對於本解法是貪心,我持保留意見!

但不管怎麼說,解法畢竟還是巧妙的,不用過於執著於其名字稱呼。

貪心算法(方法二)

可以換一個思路,首先如果總油量減去總消耗大於等於零那麼一定可以跑完一圈,說明 各個站點的加油站 剩油量rest[i]相加一定是大於等於零的。

每個加油站的剩餘量rest[i]為gas[i] - cost[i]。

i從0開始累加rest[i],和記為curSum,一旦curSum小於零,說明[0, i]區間都不能作為起始位置,起始位置從i+1算起,再從0計算curSum。

如圖:

那麼為什麼一旦[i,j] 區間和為負數,起始位置就可以是j+1呢,j+1後面就不會出現更大的負數?

如果出現更大的負數,就是更新j,那麼起始位置又變成新的j+1了。

而且j之前出現了多少負數,j後面就會出現多少正數,因為耗油總和是大於零的(前提我們已經確定了一定可以跑完全程)。

「那麼局部最優:當前累加rest[j]的和curSum一旦小於0,起始位置至少要是j+1,因為從j開始一定不行。全局最優:找到可以跑一圈的起始位置」

局部最優可以推出全局最優,找不出反例,試試貪心!

C++代碼如下:

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int curSum = 0;
        int totalSum = 0;
        int start = 0;
        for (int i = 0; i < gas.size(); i++) {
            curSum += gas[i] - cost[i];
            totalSum += gas[i] - cost[i];
            if (curSum < 0) {   // 當前累加rest[i]和 curSum一旦小於0
                start = i + 1;  // 起始位置更新為i+1
                curSum = 0;     // curSum從0開始
            }
        }
        if (totalSum < 0) return -1; // 說明怎麼走都不可能跑一圈了
        return start;
    }
};

「說這種解法為貪心算法,才是是有理有據的,因為全局最優解是根據局部最優推導出來的」

總結

對於本題首先給出了暴力解法,暴力解法模擬跑一圈的過程其實比較考驗代碼技巧的,要對while使用的很熟練。

然後給出了兩種貪心算法,對於第一種貪心方法,其實我認為就是一種直接從全局選取最優的模擬操作,思路還是好巧妙的,值得學習一下。

對於第二種貪心方法,才真正體現出貪心的精髓,用局部最優可以推出全局最優,進而求得起始位置。

就醬,「代碼隨想錄」值得推薦給身邊每一位學習算法的同學朋友,很多錄友關注後都感覺相見恨晚!

相關焦點

  • 經典算法題:猜數(360軟體測試工程師筆試題)
    又假設學生A和B的結論是正確的,則這兩個數字是:()經典算法題:《算法題 :貪心算法(大眾點評筆試題)》《算法題 :所有String2的字母在String1裡是否存在(大眾點評筆試題)》《算法題 :順序表插入新元素(迅雷筆試題)》《算法題 :迅雷2016研發工程師5道筆試題
  • 想一個月搞定面試算法?來《九章算法班》!第一節免費試聽!
    想接受系統的面試算法培訓的同學,或想換工作的但是算法比較薄弱的工程師。0算法基礎即可參與學習。主講令狐衝老師,曾就職於超過2家矽谷頂尖IT企業,  北美和國內頂尖IT企業offer數10+,面試人數超過200人。課程大綱由易到難。只要你會任何一門計算機語言即可參加。尤其適合算法基礎相對薄弱的 or 轉專業的 or 想跳槽卻太久沒刷題的同學。分九個章節,系統的講授面試中涉及的算法知識。
  • 益智遊戲剋星:BFS暴力搜索算法
    但是我們今天不來研究讓人頭禿的技巧,這些益智遊戲通通可以用暴力搜索算法解決,所以今天我們就學以致用,用 BFS 算法框架來秒殺這些遊戲。一、題目解析LeetCode 第 773 題就是滑動拼圖問題,題目的意思如下:給你一個 2x3 的滑動拼圖,用一個 2x3 的數組board表示。
  • 知其所以然之永不遺忘的算法
    於是,我們仔細研讀算法的每一步,甚至去證明算法的正確性,或者是去嘗試優雅地實現這些算法。總之,我們會花費很大的時間精力去理解這些智慧的結晶。然而,現在對於這些經典的算法你仍然瞭然於胸嗎?就算現在你仍然記得這些算法的步驟,你敢確保一年後、十年後自己不會忘記?我想沒有多少人敢保證吧。
  • 小視科技:狗臉識別算法,讓你告別狗盲症!
    小視科技針對市場需求推出「狗臉識別」算法,其通過提取狗臉特徵,計算兩張圖片狗的相似度,從而判斷是否是同一隻狗,並給出相似度的評分,實現狗臉比對。對於廣大有「狗盲症」的朋友而言,該算法將讓你徹底告別狗盲問題。其較之於目前流行的寵物電子晶片更省錢、更方便。
  • 在黑加油站明火煮飯?
    ☞詳情可諮詢付一梅18524112128微信同號☜    黑加油站藏身無名廠房,夫妻搭檔非法經營,不僅居住在站內,竟然還用明火煮食。近日,廣州增城警方在開展「颶風2020」專項行動中,打掉一個非法加油窩點,抓獲2名犯罪嫌疑人,查獲近4噸成品柴油,以及加油機、大型油罐等作案工具一批。
  • 知識拓展:recommendation algorithm(推薦算法)讓你不知不覺深陷APP
    第四段提到了recommendation algorithm(推薦算法),你知道它可以用來做什麼嗎?推薦算法是計算機專業中的一種算法,主要基於用戶的行為數據,通過一些數學算法,推測出用戶可能喜歡的東西。
  • 財務管理:成本計算--加權平均算法
    移動加權平均算法大多數的ERP使用的是加權平均算法,現在我們來舉個例子解釋一下加權平均算法步驟1:入庫2支鉛筆,每支
  • 計算機視覺:從入門到精通,極限剖析圖像識別學習算法
    本次課程將圍繞著計算機視覺中最常見的RCNN圖像識別算法進行極限剖析,從數學理論, 模型框架到實踐實操,讓你在短時間內從理論到實踐,掌握深度學習的基本知識和學習方法。· 目的:掌握神經網絡的基本原理,知其然亦知其所以然(從數學實踐到代碼的熟練和精通); · 手段:科學的方法。
  • Facebook 新算法:惡語也分三六九等
    但在整改前,公司的算法和制度並沒有對不同群體加以區分,系統無法判斷哪些群體易受仇恨言論的攻擊,哪些群體不屬於長期邊緣化人群。像「白人愚蠢」這樣的評論甚至會與反猶太主義和種族歧視並為一談。 多年來,民權倡導者一直在抨擊Facebook大量清除黑人用戶帖子的現象,這種情況在黑人描述歧視經歷時猶為明顯,此次算法修改也是對這一批評的回應。
  • 邢臺一家問題加油站被依法立案處罰!
    (名單附後),並由各地依法依規對問題加油站進行立案查處。截至目前,已對39家加油站中33家查處到位,罰款90.8萬元,其餘6家正在履行處罰程序。此次專項抽查中,省生態環境廳按照儘可能涵蓋各地各類經濟組織和規模的原則進行隨機抽查,同時對2019年度抽查中存在問題的加油站(點)進行複查。全省共抽檢加油站230家、儲油庫6座、油罐車20輛。
  • 周末欣賞 日本ENEOS加油站探秘
    歡迎您加入本平臺的交流大廳「中國民營加油站論壇」QQ群(461557367);「商務信息交流」QQ群(319379081)裡可互通油站轉讓信息,與油站相關的各路油品供應商、設備廠家和服務商都可以來這裡交流;「加油站非油業務」QQ群(551751940)是一線員工交流經驗、互相學習的樂園
  • 曝光:李滄區這家中石化加油站是假的?!
    這是明目張胆的山寨啊,掛中石化的羊頭賣私人加油站的狗肉,賣個汽油還要這麼忽悠也是沒誰了。後來往北走,到了汽車北站旁邊的中石化加油站加油聊天說起,加油站員工說,那就是個假中石化。看看下面百度地圖的顯示位置,已經有網友評論「假的!」「個人加盟的!」三次聯繫中石化客服,才得到了中國石化工作人員的正面答覆。
  • 曝光:濟寧這些加油站存在典型問題!
    1、中石化濟寧第二十四加油站現場檢查時,該加油站正在進行卸油作業,油氣回收設施未運行,壓縮機未工作,油氣回收控制系統顯示2020年10月16日未運行。4、梁山縣中澤能源加油站現場檢查時,該加油站正常營業,油氣回收裝置未開啟,開關處於關閉狀態;油氣回收裝置日常維護臺帳記錄不完善;未安裝油氣回收裝置中控設施。
  • 中心城區這些加油站問題突出……
    市環境攻堅辦派出5個督查組,對中心城區104座加油站進行了全覆蓋排查,檢查中發現民營加油站問題突出。同時,督查組對中心城區國控站點周邊292家流動攤點、早夜市、餐館、燒烤店等餐飲經營者主體進行了抽查,共發現48個餐飲經營者使用小煤爐、焦炭的行為,共收繳散煤和煤製品約1800公斤,銷毀爐具48個。
  • 今天是臺灣的「大日子」,帶你逛逛它的加油站
    相信《康熙來了》的鐵粉一定不會忘記,康熙曾做過一期關於臺灣加油站員工的專訪,加油站員工在節目裡互訴苦衷,吐槽在加油站遇到的各種奇葩事件。比如這位:還有這位:而說到加油站,除了兩家自營的加油站之外,他們也歡迎小型企業或私人加盟,因此在臺灣街頭,我們往往還能看到優力加油站,福懋加油站等等。
  • 中石油加油站竟然能「空中加油」?
    NO NO NO~~~首先帶大家了解下這座網紅加油站叫做鳳凰加油站幾個月前它還是一座普普通通的加油站,經過改造之後,鳳凰「涅槃」了是不是等不及了?其中膠管卷輪系統懸掛在加油站罩棚上,手持終端安裝在加油槍上方控制加油槍位置、加油數量、讀取IC卡等,顯示屏、按鈕開關和急停開關安裝在加油罩棚立柱上。加油操作時,就像使用老式拉繩開關電燈一樣,拉下手持終端下端的拉繩開關後,油槍下降到最低加油位,在油槍下降的過程中,接住加油槍放在油箱內,輸入相應定量數值或直接按「確認」鍵開始加油。
  • 網易遊戲面試題:如何設計一個公平的洗牌算法
    )。我記得在面試網易遊戲的時候,面試官當時是這樣問我的:「給你一副撲克牌,你能設計一個公平的洗牌算法嗎?」。大致代碼如下:import java.util.Random; class ShuffleCards {   // 洗牌算法    public static void shuffle(int card[], int n)     {
  • 門臉寫著「中石油」,發票卻是宏泰加油站!安達這個加油站居然是個冒牌貨!
    安達市民 孟先生那個加油站吧,現在的名字叫中國石油,但是他開發票叫宏泰加油站,那就是欺騙消費者唄,中國石油的油肯定好啊。根據孟先生提供的線索,記者找到了這家加油站,從外表看上去,門臉和標識同中國石油的加油站沒什麼兩樣。綏化農墾宏泰加油站 工作人員(記者:是中石油的加油站嗎?)
  • 汝南警方成功打掉一黑加油站
    近日,汝南縣公安局接到群眾舉報稱:G230國道汝南縣常興鎮小亮寺村有一黑加油站,經營石油、柴油、液化氣等業務,存在重大安全隱患。,組成30餘人的聯合執法工作組,在常興鎮政府的通力配合下,對該家黑加油站進行打擊整治。