| 題引
俄羅斯套娃是俄羅斯特產的木製玩具,一般由多個一樣圖案的空心木娃娃一個套一個組成,最多可達十多個,通常爲圓柱形,底部平坦可以直立。顏色有紅色,藍色,綠色,紫色等。最普通的圖案是一個穿着俄羅斯民族服裝的姑娘,叫做“瑪特羅什卡”,這也成爲這種娃娃的通稱(如下圖所示)。
| 一維套娃 - 最長上升子序列
正題
給定一個無序的整數數組,找到其中最長上升子序列的長度
如:
輸入: [2,6,1,9]
輸出: 3
解釋: 最長的上升子序列是[2,6,9],它的長度是 3。
| 二維套娃 - 套娃信封
正題
給定一些標記了寬度和高度的信封,寬度和高度都爲正整數。
當另一個信封的寬度和高度都比這個信封大的時候,這個信封就可以放進另一個信封裏,如同俄羅斯套娃一樣,不允許旋轉信封!
計算最多能有多少個信封能組成一組“俄羅斯套娃”信封。
如:
輸入: [[5,4],[6,4],[6,7],[2,3]]
輸出: 3
解釋: 最多信封的個數爲 3, 組合爲: [2,3] => [5,4] => [6,7]。
| 要點
一維套娃
- 順序不能改變
- 可能會有多個上升子序列,需要找到最長的那一個
二維套娃
- 順序可以改變
- 不能旋轉
- 寬度和長度必須同時是上升子序列
| 分析 - 最長上升子序列
一維套娃
2,6,1,9可以組成的上升子序列爲 [2, 6, 9] 和 [1, 9]
(其中一個數字暫不考慮,2,6和6,9可以算上升子序列,但屬於2,6,9,我們就可以不考慮這種組合了)
最長情況爲[2, 6, 9] ,所以長度是3
我們使用逆向思維推理一下(關於逆向思維可以看一下前一篇“趣味”or“燒腦”算法 之 王子救公主)
如果我要計算出9這個位置最長上升子序列長度,那麼我就需要知道比9小的並且在9前面的所有的位置的最長上升子序列長度,如下圖:
再往前推,6的數字最大,我就推出6這個位置最長上升子序列長度了
以此類推…
所以程序設計時,我們從頭開始,將每一位的最長上升子序列計算出(以[10,9,2,5,3,7,98,18]爲例):
| 編碼 – 最長上升子序列
import java.util.Arrays;
/**
* @author yanghao
* @version LeetCode300Test.java, v 0.1 2019-11-19 16:44
*/
public class LeetCode300Test {
public static void main(String[] args) {
LeetCode300Test test = new LeetCode300Test();
int[] nums = new int[]{10,9,2,5,3,7,98,18};
System.out.println("result == " + test.leetCode300(nums));
}
public int leetCode300(int[] nums) {
if (nums == null || nums.length == 0) {
return 0;
}
int[] dp = new int[nums.length];
//將數組全部填充爲1
Arrays.fill(dp, 1);
int max = 0;
for (int i = 0; i < nums.length; ++i) {
for (int j = 0; j < i; ++j) {
if (nums[i] > nums[j]) {
dp[i] = Math.max(dp[j] + 1, dp[i]);
}
}
max = Math.max(max, dp[i]);
}
return max;
}
}
| 分析 - 套娃信封
二維套娃
二維的解決思路可以參考一下一維的思想,重要點就是要保證寬和高同時是上升子序列,但是初始數據可能是一個無序的,所以我們可以先把數組按照寬度排序,那麼此時,高度就可以看成是一個一維套娃了~
注意點:寬度可能是有重複存在的,重複的數字是無法進行套娃的。
先把數組按寬度排序,得到新數組sortTemp = [[2,3], [5,4], [6,4], [6,7]]
判斷條件爲sortTemp[i][0] > sortTemp[j][0] && sortTemp[i][1] > sortTemp[j][1]
即:寬帶和高度必須同時大於才能滿足
| 編碼 – 套娃信封
import java.util.Arrays;
/**
* @author yanghao
* @version LeetCode354Test.java, v 0.1 2019-11-19 11:27
*/
public class LeetCode354Test {
public static void main(String[] args) {
LeetCode354Test test = new LeetCode354Test();
int[][] envelopes = new int[4][2];
envelopes[0] = new int[]{5, 4};
envelopes[1] = new int[]{6, 4};
envelopes[2] = new int[]{6, 7};
envelopes[3] = new int[]{2, 3};
System.out.println("result == " + test.leetCode354(envelopes));
}
public int leetCode354(int[][] envelopes) {
int y = envelopes.length;
if(envelopes == null || y == 0){
return 0;
}
if(y == 1){
return 1;
}
//1.先按寬度排序
int[][] sortTemp = sortTemp(envelopes);
//2.計算最大個數
int max = getMax(sortTemp);
return max;
}
/**
* 按寬度排序
* @param envelopes
* @return
*/
private int[][] sortTemp(int[][] envelopes) {
int y = envelopes.length;
//新定義一個容器
int[][] sortTemp = new int[y][2];
sortTemp[0] = envelopes[0];
int j = 1;
for(int i = 1; i < y; i ++){
j = i;
while (j >= 1){
if(envelopes[i][0] < sortTemp[j - 1][0]){
sortTemp[j] = sortTemp[j - 1];
sortTemp[j - 1] = envelopes[i];
j --;
}else{
sortTemp[j] = envelopes[i];
j = 0;
}
}
}
printResult("sortTemp", sortTemp);
return sortTemp;
}
/**
* 計算最大長度
* @param sortTemp
* @return
*/
private int getMax(int[][] sortTemp) {
int y = sortTemp.length;
int max = 1;
int[] dp = new int[y];
Arrays.fill(dp, 1);
for(int i = 0; i < y; i ++){
for(int j = 0; j < i; j ++){
if(sortTemp[i][0] > sortTemp[j][0] && sortTemp[i][1] > sortTemp[j][1]){
//
dp[i] = Math.max(dp[j] + 1, dp[i]);
}
}
max = Math.max(max, dp[i]);
}
return max;
}
private void printResult(String tempStr, int[][] temps) {
System.out.println("===== " + tempStr + " ===== ");
for(int[] temp : temps){
System.out.println(temp[0] + " " + temp[1]);
}
}
}
| 總結
有趣的套娃算法,如果能get到點,應該還是很好理解的,當然了,鼓勵大家能夠多想一些更好的其他解決方案
(案例選取leetcode300題和354題)
有興趣的小夥伴們,可以研究一下三維套娃,嘿嘿😁~