信封巢狀問題

2022-07-04 04:00:09 字數 1898 閱讀 3970

很多演算法問題都需要排序技巧,其難點不在於排序本身,而是需要巧妙地排序進行預處理,將演算法問題進行轉換,為之後的操作打下基礎。

信封巢狀問題就需要先按特定的規則排序,之後就轉換為乙個 最長遞增子串行問題,可以用前文 二分查詢詳解 的技巧來解決了。

信封巢狀問題是個很有意思且經常出現在生活中的問題,先看下題目:

這道題目其實是最長遞增子串行(longes increasing subsequence,簡寫為 lis)的乙個變種,因為很顯然,每次合法的巢狀是大的套小的,相當於找乙個最長遞增的子串行,其長度就是最多能巢狀的信封個數。

但是難點在於,標準的 lis 演算法只能在陣列中尋找最長子序列,而我們的信封是由(w, h)這樣的二維數對形式表示的,如何把 lis 演算法運用過來呢?

讀者也許會想,通過w × h計算面積,然後對面積進行標準的 lis 演算法。但是稍加思考就會發現這樣不行,比如1 × 10大於3 × 3,但是顯然這樣的兩個信封是無法互相巢狀的。

這道題的解法是比較巧妙的:

先對寬度w進行公升序排序,如果遇到w相同的情況,則按照高度h降序排序。之後把所有的h作為乙個陣列,在這個陣列上計算 lis 的長度就是答案。

畫個圖理解一下,先對這些數對進行排序:

然後在h上尋找最長遞增子串行:

這個子串行就是最優的巢狀方案。

這個解法的關鍵在於,對於寬度w相同的數對,要對其高度h進行降序排序。因為兩個寬度相同的信封不能相互包含的,逆序排序保證在w相同的數對中最多隻選取乙個。

下面看**:

// envelopes = [[w, h], [w, h]...]

public int maxenvelopes(int envelopes)

});// 對高度陣列尋找 lis

int height = new int[n];

for (int i = 0; i < n; i++)

height[i] = envelopes[i][1];

return lengthoflis(height);

}

/* 返回 nums 中 lis 的長度 */

public int lengthoflis(int nums)

if (left == piles) piles++;

// 把這張牌放到牌堆頂

top[left] = poker;

}// 牌堆數就是 lis 長度

return piles;

}

為了清晰,我將**分為了兩個函式, 你也可以合併,這樣可以節省下height陣列的空間。

此演算法的時間複雜度為 \(o(nlogn)\),因為排序和計算 lis 各需要 \(o(nlogn)\) 的時間。

空間複雜度為 \(o(n)\),因為計算 lis 的函式中需要乙個top陣列。

這個問題是個 hard 級別的題目,難就難在排序,正確地排序後此問題就被轉化成了乙個標準的 lis 問題,容易解決一些。

其實這種問題還可以拓展到三維,比如說現在不是讓你巢狀信封,而是巢狀箱子,每個箱子有長寬高三個維度,請你算算最多能巢狀幾個箱子?

我們可能會這樣想,先把前兩個維度(長和寬)按信封巢狀的思路求乙個巢狀序列,最後在這個序列的第三個維度(高度)找一下 lis,應該能算出答案。

實際上,這個思路是錯誤的。這類問題叫做「偏序問題」,上公升到三維會使難度巨幅提公升,需要借助一種高階資料結構「樹狀陣列」,有興趣的讀者可以自行搜尋。

信封巢狀問題

給n個信封的長度和寬度。如果信封a的長和寬都小於信封b,那麼信封a可以放到信封b裡,請求出信封最多可以巢狀多少層。輸出包含多行,第一行包括乙個整數,代表信封的個數n 1 n 100000 接下來n行,每行兩個整數li和wi 代表信封的長度和寬度 1e9 include includeusing na...

信封巢狀問題

354.俄羅斯套娃信封問題 很多演算法問題都需要排序技巧,其難點不在於排序本身,而是需要巧妙地排序進行預處理,將演算法問題進行轉換,為之後的操作打下基礎。信封巢狀問題就需要先按特定的規則排序,之後就轉換為乙個 最長遞增子串行問題 的技巧來解決了。信封巢狀問題是個很有意思且經常出現在生活中的問題,先看...

listview巢狀問題

publicstaticvoidsetlistviewheightbasedonchildren listview listview inttotalheight 0 for inti 0,len listadapter.getcount i len i viewgroup.layoutparams...