最大化最小值和最小化最大值
最小化最大值是為了壓制優化目標中表現最突出的成分,最大化最小值為了提升優化目標中表現最差的成分
關于這兩者的理解,我覺得這篇博文講得非常好,淺顯易懂又聯系實際。
理解問題后,就要思考如何解決問題。
記住,這兩類問題一般都是用問題轉換加二分查找的方法解決。
我會用代碼+詳細注釋的形式記錄這兩類問題的解法,題目描述易于理解,耐心看完再看題解才會有收獲。
最大化最小值問題:leetcode
你有一大塊巧克力,它由一些甜度不完全相同的小塊組成。我們用數組 sweetness 來表示每一小塊的甜度。
你打算和 K 名朋友一起分享這塊巧克力,所以你需要將切割 K 次才能得到 K+1 塊,每一塊都由一些 連續 的小塊組成。
為了表現出你的慷慨,你將會吃掉 總甜度最小 的一塊,并將其余幾塊分給你的朋友們。
請找出一個最佳的切割策略,使得你所分得的巧克力總甜度最大,并返回這個 最大總甜度。
?
示例 1:
輸入:sweetness = [1,2,3,4,5,6,7,8,9], K = 5
輸出:6
解釋:你可以把巧克力分成 [1,2,3], [4,5], [6], [7], [8], [9]。
示例 2:
輸入:sweetness = [5,6,7,8,9,1,2,3,4], K = 8
輸出:1
解釋:只有一種辦法可以把巧克力分成 9 塊。
示例 3:
輸入:sweetness = [1,2,2,1,2,2,1,2,2], K = 2
輸出:5
解釋:你可以把巧克力分成 [1,2,2], [1,2,2], [1,2,2]。
?
提示:
?? ?0 <= K < sweetness.length <= 10^4
?? ?1 <= sweetness[i] <= 10^5
?
題目描述中我用下劃線標注出來的語句點明了這是一道最大化最小值的問題。拋開題目情景,這道題其實可以描述為:
給定一個數組,將數組分割成K+1個連續的子數組,求一種分割方法可以使得分割后的所有子數組的和的最小值,比其他分割方法得到的子數組的和最小值都大。要求輸出這個最大的最小值。
我們知道一個數組的子數組可以是它本身,也可以是一個只包含數組中任一元素的數組。所以我們可以求出數組的最小值(設為A)以及數組的和(設為B),那么我們要找的最大的最小值必定屬于[A,B)。
因此,問題可以轉為在[A,B)中找到一個最大的數(最大化)使得存在一種分割方法可以讓所有子數組的和大于或等于這個數(最小值)。
由于[A,B)是有序的,所以在有序序列中找數,可以用二分查找。
那么如何找到上述的分割方法?
首先確定一點,這種分割方法要讓所有子數組的和大于或等于某個數(設為N),那么我們就要盡量讓其中的子數組的和與M的差值小一些。假設有一個子數組的和遠遠超過N,那就說明其他子數組可以分到的元素很少,難以保證其他子數組的和也能超過M。這就像有時候我們在分配物資一樣(前提是所有物資都必須分配完),在物資充足的情況下,我們說要讓每個人都至少有N件物資,結果一開始分配的時候就有一老哥上來拿走了遠超過N件的物資然后就溜了,結果剩下的物資就難以保證每個人都至少有N件了,剩下的人肯定不愿意。所以一開始分配給那位老哥的時候,我們就不能讓他自己拿,應該一件一件地給他,給到N件了就讓他走人,這樣才能保證最后大家都有至少有N件。而如果一開始物資就不足以讓每個人都至少有N件,那按照這個方法分到最后肯定有人沒有拿到N件物資。這樣的話,我們通過記錄多少人拿了N件物資,就能夠區分我們手頭的物資到底能不能讓每個人都至少有N件。
上面的描述轉換為我們討論的數組和子數組,就是對于第一個子數組,我們依次分配給它原數組的元素直到它的和超過了N,那我們就不分配了,開始給第二個子數組分配了。最終看有多少個子數組的和大于或等于N。假設我們要求應該有T個這樣的子數組,而實際上根據我們的分配得到的滿足要求的子數組的個數是M。若M>T,則說明以N為最小值完全可以滿足我們的要求,N甚至可以再大些;若M<T,則說明我們規定的N太大了,滿足不了這樣的分配,得把N設得小一點。M=T的情況可以歸到M>T中,在下面的代碼后會說明(結合代碼說比較清楚)。
鋪墊了這么多,可以寫代碼了:
class Solution { public:int maximizeSweetness(vector<int>& sweetness, int K) {int left=100005,right=0; //在[left,right)中通過二分查找尋找我們要的值for(int sw:sweetness){left=min(left,sw);right+=sw;}while(left<right){ //為避免出現 (left+right)/2=left,然后cancut又一直返回true的情況,采用left+right+1int mid=(left+right+1)/2; //二分查找,mid就是我們每次給子數組的和設置的最小值if(cancut(sweetness,K,mid)){ //cancut=true,可以根據mid來切割,試試換個大一點的midleft=mid;}else{right=mid-1;}}return left;}//cuts:我們需要切割的次數;target:每個子數組的和要大于或等于target//返回值:true=target設置得太小了,再大點也能滿足要求;false則相反bool cancut(vector<int>& sweetness,int cuts,int target){int sum=0,cut=0; //sum記錄子數組的和, cnt記錄滿足要求的子數組的個數for(int sw:sweetness){sum+=sw;if(sum>=target){ //滿足要求了,割它sum=0; ++cnt;}if(cnt>cuts) return true;}return false;} };上面代碼中的cancut函數為什么不考慮cnt=cuts+1的情況(即達到要求的子數組個數與題目要求的個數相同)?這種情況下的target就是我們要找的最大化最小值嗎?
不是的,因為有可能數組的最后一個數組很大使得最后一個子數組的和超過2*target,那么實際上target還可以再設置為更大點。
?
最小化最大值問題:leetcode
給定一個非負整數數組和一個整數 m,你需要將這個數組分成 m 個非空的連續子數組。設計一個算法使得這 m 個子數組各自和的最大值最小。
注意:
數組長度 n 滿足以下條件:
?? ?1 ≤ n ≤ 1000
?? ?1 ≤ m ≤ min(50, n)
示例:?
輸入:
nums = [7,2,5,10,8]
m = 2
輸出:
18
解釋:
一共有四種方法將nums分割為2個子數組。
其中最好的方式是將其分為[7,2,5] 和 [10,8],
因為此時這兩個子數組各自的和的最大值為18,在所有情況中最小。
?
這題題目描述已經相當明確了,沒有最大化最小值那題的情景設計。
我在一開始說過,這兩類問題的基本思路是一樣的,核心是二分查找。看了上面我的那一大段最大化最小值的解釋后應該很快能類比出這題的思路。
首先明確,要求的是“最大值”,其次才是最小化的最大值。那么假設原數組中最大的元素值為A,數組的和為B,那我們要求的這個最大值一定屬于[A,B),所以我們在[A,B)中二分查找。
要使最大值最小,那么在分配子數組時我們應該盡量讓每個子數組足夠的大。
還是拿分配物資來舉例。現在我們的目標是讓分配給某個人的物資不能超過K件(前提是所有物資都必須分配完)。那么如果一開始我們分配給前面的人的時候分配得很少,那么后面的人就越有可能分配到超過K件物資。因此我們要從一開始就盡可能地多分配物資,但不超過K件。
回到數組中,即我們要讓第一個子數組盡可能的接近K但不超過K(可以等于,這時這個數組可能就是那個最小化最大值的子數組和了),當再分配一個元素給第一個子數組時,其和超過K,那這個元素我們不分配給它,我們分配給第二個子數組,以此類推。
若最終分割到的子數組的個數為M,而我們要求的子數組的個數為T。當M>T時,說明有太多的子數組的和接近K,我們可以讓K再大一點,以減少M。當M<T時,說明接近K的子數組和太少了,我們應該讓K小一點,以來更多的子數組的和接近K。
下面的代碼采用long long做運算是因為不確定數據的取值范圍,怕發生整數溢出。
class Solution { public:int splitArray(vector<int>& nums, int m) {long long left=0,right=0;for(int num:nums) //取最大元素值和數組和left=max(left,(long long)num),right+=num;while(left<right){long long mid=(left+right)/2;if(cancut(nums,m-1,mid))right=mid;elseleft=mid+1;}return (int)left;}bool cancut(vector<int>& nums,int cuts,long long target){long long sum=0;int cnt=0;for(int num:nums){sum+=num;if(sum>target){ //target是預設的最大值,當前子數組的和加上num之后超過了target,這是絕對不行的,所以我們在此把它割了++cnt;sum=num; //然后把num分配給下一個子數組}if(cnt>cuts) //這就是M>=T的情況,至于>=為什么可以歸于一類,道理同最大化最小值最后我解釋的一樣return false;}return true;}};如果你要問怎么上面代碼的二分就不用left+right+1,那你得好好溫習一下二分查找了,二分查找雖然簡單,但有時候自己實現起來還說不定bug重重,不斷TLE。
希望從此我能很快解決這兩類問題!從理解到熟記于心!
總結
以上是生活随笔為你收集整理的最大化最小值和最小化最大值的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LGBM函数及参数详解
- 下一篇: 腾讯云如何判断服务器是否中毒以及如何预防