1. 程式人生 > 其它 >leetcode - 1691 - 堆疊長方體的最大高度 - 動態規劃 - 模擬 - 圖

leetcode - 1691 - 堆疊長方體的最大高度 - 動態規劃 - 模擬 - 圖

技術標籤:leetcodeleetcode動態規劃圖論

歡迎關注更多精彩
關注我,學習常用演算法與資料結構,一題多解,降維打擊。

文章目錄

題目描述

[[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 。

總高度是 95 + 50 + 45 = 190 。
示例 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

Related Topics
  • 模擬
  • 動態規劃
  • 記憶化搜尋
  • 貪心

題目剖析&資訊挖掘

此題為動態規劃,可以結合貪心+模擬的思路解決,可以參考一下最長遞增子序列那一題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/最長遞增子序列
    ``
    在這裡插入圖片描述