C++最大容量問題

2023-09-20 15:43 更新

Question

輸入一個數(shù)組 ht ,數(shù)組中的每個元素代表一個垂直隔板的高度。數(shù)組中的任意兩個隔板,以及它們之間的空間可以組成一個容器。

容器的容量等于高度和寬度的乘積(即面積),其中高度由較短的隔板決定,寬度是兩個隔板的數(shù)組索引之差。

請在數(shù)組中選擇兩個隔板,使得組成的容器的容量最大,返回最大容量。

最大容量問題的示例數(shù)據(jù)

圖 15-7   最大容量問題的示例數(shù)據(jù)

容器由任意兩個隔板圍成,因此本題的狀態(tài)為兩個隔板的索引,記為 [i,j]

根據(jù)題意,容量等于高度乘以寬度,其中高度由短板決定,寬度是兩隔板的索引之差。設(shè)容量為 cap[i,j] ,則可得計算公式:

cap[i,j]=min(ht[i],ht[j])×(j?i)

設(shè)數(shù)組長度為 n ,兩個隔板的組合數(shù)量(即狀態(tài)總數(shù))為 Cn2=n(n?1)2 個。最直接地,我們可以窮舉所有狀態(tài),從而求得最大容量,時間復(fù)雜度為 O(n2) 。

1.   貪心策略確定?

這道題還有更高效率的解法。如圖 15-8 所示,現(xiàn)選取一個狀態(tài) [i,j] ,其滿足索引 i<j 且高度 ht[i]<ht[j] ,即 i 為短板、j 為長板。

初始狀態(tài)

圖 15-8   初始狀態(tài)

如圖 15-9 所示,若此時將長板 j 向短板 i 靠近,則容量一定變小

這是因?yàn)樵谝苿娱L板 j 后,寬度 j?i 肯定變小;而高度由短板決定,因此高度只可能不變( i 仍為短板)或變小(移動后的 j 成為短板)。

向內(nèi)移動長板后的狀態(tài)

圖 15-9   向內(nèi)移動長板后的狀態(tài)

反向思考,我們只有向內(nèi)收縮短板 i ,才有可能使容量變大。因?yàn)殡m然寬度一定變小,但高度可能會變大(移動后的短板 i 可能會變長)。例如在圖 15-10 中,移動短板后面積變大。

向內(nèi)移動短板后的狀態(tài)

圖 15-10   向內(nèi)移動短板后的狀態(tài)

由此便可推出本題的貪心策略:初始化兩指針分裂容器兩端,每輪向內(nèi)收縮短板對應(yīng)的指針,直至兩指針相遇。

圖 15-11 展示了貪心策略的執(zhí)行過程。

  1. 初始狀態(tài)下,指針 ij 分列與數(shù)組兩端。
  2. 計算當(dāng)前狀態(tài)的容量 cap[i,j] ,并更新最大容量。
  3. 比較板 i 和 板 j 的高度,并將短板向內(nèi)移動一格。
  4. 循環(huán)執(zhí)行第 2.3. 步,直至 ij 相遇時結(jié)束。

最大容量問題的貪心過程max_capacity_greedy_step2max_capacity_greedy_step3

max_capacity_greedy_step4

max_capacity_greedy_step5

max_capacity_greedy_step6

max_capacity_greedy_step7max_capacity_greedy_step8max_capacity_greedy_step9

圖 15-11   最大容量問題的貪心過程

2.   代碼實(shí)現(xiàn)

代碼循環(huán)最多 n 輪,因此時間復(fù)雜度為 O(n) 。

變量 i、j、res 使用常數(shù)大小額外空間,因此空間復(fù)雜度為 O(1) 。

max_capacity.cpp

/* 最大容量:貪心 */
int maxCapacity(vector<int> &ht) {
    // 初始化 i, j 分列數(shù)組兩端
    int i = 0, j = ht.size() - 1;
    // 初始最大容量為 0
    int res = 0;
    // 循環(huán)貪心選擇,直至兩板相遇
    while (i < j) {
        // 更新最大容量
        int cap = min(ht[i], ht[j]) * (j - i);
        res = max(res, cap);
        // 向內(nèi)移動短板
        if (ht[i] < ht[j]) {
            i++;
        } else {
            j--;
        }
    }
    return res;
}

3.   正確性證明

之所以貪心比窮舉更快,是因?yàn)槊枯喌呢澬倪x擇都會“跳過”一些狀態(tài)。

比如在狀態(tài) cap[i,j] 下,i 為短板、j 為長板。若貪心地將短板 i 向內(nèi)移動一格,會導(dǎo)致圖 15-12 所示的狀態(tài)被“跳過”。這意味著之后無法驗(yàn)證這些狀態(tài)的容量大小。

cap[i,i+1],cap[i,i+2],,cap[i,j?2],cap[i,j?1]

移動短板導(dǎo)致被跳過的狀態(tài)

圖 15-12   移動短板導(dǎo)致被跳過的狀態(tài)

觀察發(fā)現(xiàn),這些被跳過的狀態(tài)實(shí)際上就是將長板 j 向內(nèi)移動的所有狀態(tài)。而在第二步中,我們已經(jīng)證明內(nèi)移長板一定會導(dǎo)致容量變小。也就是說,被跳過的狀態(tài)都不可能是最優(yōu)解,跳過它們不會導(dǎo)致錯過最優(yōu)解

以上的分析說明,移動短板的操作是“安全”的,貪心策略是有效的。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號