leetcode - 1691 - 堆疊長方體的最大高度 - 動態規劃 - 模擬 - 圖
歡迎關注更多精彩
關注我,學習常用演算法與資料結構,一題多解,降維打擊。
文章目錄
題目描述
[[1691] 堆疊長方體的最大高度
- https://leetcode-cn.com/problems/maximum-height-by-stacking-cuboids/
給你 n 個長方體 cuboids ,其中第 i 個長方體的長寬高表示為 cuboids[i] = [widthi, lengthi, heighti](下標從 0 開始)。請你從 cuboids 選出一個 子集 ,並將它們堆疊起來。
如果 widthi <= widthj 且 lengthi <= lengthj 且 heighti <= heightj ,你就可以將長方體 i 堆疊在長方體 j 上。你可以通過旋轉把長方體的長寬高重新排列,以將它放在另一個長方體上。
返回 堆疊長方體 cuboids 可以得到的 最大高度 。
示例 1:
輸入:cuboids = [[50,45,20],[95,37,53],[45,23,12]]
輸出:190
解釋:
第 1 個長方體放在底部,53x37 的一面朝下,高度為 95 。
第 0 個長方體放在中間,45x20 的一面朝下,高度為 50 。
第 2 個長方體放在上面,23x12 的一面朝下,高度為 45 。
示例 2:
輸入:cuboids = [[38,25,45],[76,35,3]]
輸出:76
解釋:
無法將任何長方體放在另一個上面。
選擇第 1 個長方體然後旋轉它,使 35x3 的一面朝下,其高度為 76 。
示例 3:
輸入:cuboids = [[7,11,17],[7,17,11],[11,7,17],[11,17,7],[17,7,11],[17,11,7]]
輸出:102
解釋:
重新排列長方體後,可以看到所有長方體的尺寸都相同。
你可以把 11x7 的一面朝下,這樣它們的高度就是 17 。
堆疊長方體的最大高度為 6 * 17 = 102 。
提示:
n == cuboids.length
1 <= n <= 100
1 <= widthi, lengthi, heighti <= 100
- 模擬
- 動態規劃
- 記憶化搜尋
- 貪心
題目剖析&資訊挖掘
此題為動態規劃,可以結合貪心+模擬的思路解決,可以參考一下最長遞增子序列那一題https://leetcode-cn.com/problems/longest-increasing-subsequence/。
解題思路
方法一 貪心模擬法
分析
- dp(i, j) 表示以第i個箱子為底,並且以第j第邊為高旋轉時,所能到達的最大高度。
- dp(i, j) = cuboids[i][j] + max(dp(x, 0), dp(x, 1), dp(x, 2)) 其中 x 個箱子必須以0,1,2號邊為高時要能放在 i 箱子上。
- 以這個為條件可以形成一張圖
- dp的過程就是去遍歷這張圖。
- 最終答案就是max(dp(i, j)) i = [0,n), j=[0,2]。
思路與優化
-
總結一下思路如下
-
class Solution { private: int h[n][3]; // 儲存dp結果 bool vis[n][3]; // 記憶標記 bool canPushOn(vector<int> upCube, int upHeightInd, vector<int> downCube, int downHeightInd) { } int dp(int i, int j, vector<vector<int>> &cuboids) { if (vis[i][j]) return h[i][j]; vis[i][j] = true; int topheight = 0; // 檢視其他箱子並嘗試將其放到自己上頭 for (int k = 0; k < cuboids.size(); ++k) { for (int l = 0; l < 3; ++l) { // 以l為高放置 // 判斷是否可以放在當前箱子上面 if (!canPushOn(cuboids[k], l, cuboids[i], j)) continue; topheight = max(topheight, dp(k, l, cuboids)); } } h[i][j] = cuboids[i][j] + topheight; return h[i][j]; } public: int maxHeight(vector<vector<int>> &cuboids) { for (int i = 0; i < cuboids.size(); ++i) { memset(vis[i], 0, sizeof(vis[i])); } int maxH = 0; for (int i = 0; i < cuboids.size(); ++i) { maxH = max(maxH, dp(i, 0, cuboids)); maxH = max(maxH, dp(i, 1, cuboids)); maxH = max(maxH, dp(i, 2, cuboids)); } return maxH; } };
-
但是以上演算法還有一個缺點,以[[1,1,1],[1,1,1]] 為例,可以發現執行這個例子的時候會存在迴圈,導致答案錯誤。
-
回想一下最長上升序列那題其實我們dp的方向都是向前的,也就是i是越來越小的。
-
那對於這一題來說只要2個三維體的長寬高不一樣,那麼這2者之間的上下關係是固定的。
-
舉例說明一下
-
cube1 [a,b,c], cube2 [d,e,f] 其中 a<b<c, d<e<f ,並假設a<d。這裡有一個結論就是cube1 最多隻能放在cube2上面(不可能的情況是cube1放在cube2的下面)。
-
證明:如果cube1想放在cube2下面,那麼首先要找到一條cube2邊比a小,顯然是不可能的。
-
有了以上結論我們可以先對所有的cube按照長寬高排序,我們規定高>寬>長。
-
然後從前往後遍歷cube再去做dp。
-
這樣也能解決我們之前說的2個相同規格箱子的case。
-
優化後的思路
class Solution {
private:
static bool Less1(vector<int> &s1, vector<int> &s2) {
}
int h[n][3]; // 儲存dp結果
bool vis[n][3]; // 記憶標記
bool canPushOn(vector<int> upCube, int upHeightInd, vector<int> downCube, int downHeightInd) {
}
int dp(int i, int j, vector<vector<int>> &cuboids) {
if (vis[i][j]) return h[i][j];
vis[i][j] = true;
int topheight = 0;
// 檢視其他箱子並嘗試將其放到自己上頭
for (int k = 0; k < i; ++k) {
for (int l = 0; l < 3; ++l) { // 以l為高放置
// 判斷是否可以放在當前箱子上面
if (!canPushOn(cuboids[k], l, cuboids[i], j)) continue;
topheight = max(topheight, dp(k, l, cuboids));
}
}
h[i][j] = cuboids[i][j] + topheight;
return h[i][j];
}
public:
int maxHeight(vector<vector<int>> &cuboids) {
for (int i = 0; i < cuboids.size(); ++i) {
memset(vis[i], 0, sizeof(vis[i]));
}
int maxH = 0;
sort(cuboids.begin(), cuboids.end(), Less1);
for (int i = 0; i < cuboids.size(); ++i) {
maxH = max(maxH, dp(i, 0, cuboids));
maxH = max(maxH, dp(i, 1, cuboids));
maxH = max(maxH, dp(i, 2, cuboids));
}
return maxH;
}
};
知識點
- 貪心
- 模擬
- 動態規劃
複雜度
- 時間複雜度:O(n^2)
- 空間複雜度:O(n^2)
程式碼實現
class Solution {
private:
static bool Less1(vector<int> &s1, vector<int> &s2) {
sort(s1.begin(), s1.end());
sort(s2.begin(), s2.end());
for (int i = 0; i < 3; ++i) {
if (s1[i] < s2[i]) return true;
if (s1[i] > s2[i]) return false;
}
return false;
}
int h[n][3]; // 儲存dp結果
bool vis[n][3]; // 記憶標記
void getLongWeight(vector<int> &cube, int heightInd, int a[2]) {
for (int i = 0, j = 0; i < 3; ++i) {
if (i == heightInd)continue;
a[j++] = cube[i];
}
sort(a, a + 2); // 排序,使得小邊與小邊比,大邊與大邊比
}
bool canPushOn(vector<int> upCube, int upHeightInd, vector<int> downCube, int downHeightInd) {
if (upCube[upHeightInd] > downCube[downHeightInd]) return false; // 判斷高度是否滿足條件
// 採集剩下2邊
int upLongeWeight[2], downLongeWeight[2];
getLongWeight(upCube, upHeightInd, upLongeWeight);
getLongWeight(downCube, downHeightInd, downLongeWeight);
// 判斷條件
return upLongeWeight[0] <= downLongeWeight[0] && upLongeWeight[1] <= downLongeWeight[1];
}
int dp(int i, int j, vector<vector<int>> &cuboids) {
if (vis[i][j]) return h[i][j];
vis[i][j] = true;
int topheight = 0;
// 檢視其他箱子並嘗試將其放到自己上頭
for (int k = 0; k < i; ++k) {
for (int l = 0; l < 3; ++l) { // 以l為高放置
// 判斷是否可以放在當前箱子上面
if (!canPushOn(cuboids[k], l, cuboids[i], j)) continue;
topheight = max(topheight, dp(k, l, cuboids));
}
}
h[i][j] = cuboids[i][j] + topheight;
return h[i][j];
}
public:
int maxHeight(vector<vector<int>> &cuboids) {
for (int i = 0; i < cuboids.size(); ++i) {
memset(vis[i], 0, sizeof(vis[i]));
}
int maxH = 0;
sort(cuboids.begin(), cuboids.end(), Less1);
/*
for (int j = 0; j < cuboids.size(); ++j) {
for (int i = 0; i < 3; ++i) {
cout << cuboids[j][i] << ' ';
}
cout << endl;
}
*/
for (int i = 0; i < cuboids.size(); ++i) {
maxH = max(maxH, dp(i, 0, cuboids));
maxH = max(maxH, dp(i, 1, cuboids));
maxH = max(maxH, dp(i, 2, cuboids));
}
return maxH;
}
};
相關題目
- https://leetcode-cn.com/problems/longest-increasing-subsequence/最長遞增子序列
``