leetcode 1000 合併石頭的最低成本

原題:
有 N 堆石頭排成一排,第 i 堆中有 stones[i] 塊石頭。

每次移動(move)需要將連續的 K 堆石頭合併爲一堆,而這個移動的成本爲這 K 堆石頭的總數。

找出把所有石頭合併成一堆的最低成本。如果不可能,返回 -1 。

示例 1:

輸入:stones = [3,2,4,1], K = 2
輸出:20
解釋:
從 [3, 2, 4, 1] 開始。
合併 [3, 2],成本爲 5,剩下 [5, 4, 1]。
合併 [4, 1],成本爲 5,剩下 [5, 5]。
合併 [5, 5],成本爲 10,剩下 [10]。
總成本 20,這是可能的最小值。

示例 2:

輸入:stones = [3,2,4,1], K = 3
輸出:-1
解釋:任何合併操作後,都會剩下 2 堆,我們無法再進行合併。所以這項任務是不可能完成的。.

示例 3:

輸入:stones = [3,5,1,2,6], K = 3
輸出:25
解釋:
從 [3, 5, 1, 2, 6] 開始。
合併 [5, 1, 2],成本爲 8,剩下 [3, 8, 6]。
合併 [3, 8, 6],成本爲 17,剩下 [17]。
總成本 25,這是可能的最小值。

提示:

1 <= stones.length <= 30
2 <= K <= 30
1 <= stones[i] <= 100

代碼:

class Solution {
public:
    int dp[51][51][51];
    int mergeStones(vector<int>& stones, int K) 
    {
        int n = stones.size();
        if ((n - K) % (K - 1))
            return -1;
        vector<int> tmp(stones.size() + 1, 0);
        for (int i = 0; i < stones.size(); i++)
            tmp[i + 1] = stones[i];
        
        memset(dp, 0x3f, sizeof(dp));
        vector<int> sum(stones.size()+1,0);
        for (int i = 1; i <= n; i++)
        {
            dp[i][i][1] = 0;
            sum[i] = sum[i - 1] + tmp[i];
        }
        for (int len = 2; len <= n; len++)
        {
            for (int i = 1; i <= n - len + 1; i++)
            {
                int j = i + len - 1;
                for (int x = i; x < j; x++)
                {
                    for (int k = 2; k <= len; k++)
                    {
                        dp[i][j][k] = min(dp[i][j][k], dp[i][x][k - 1] + dp[x + 1][j][1]);
                    }
                }
                dp[i][j][1] = min(dp[i][j][1], dp[i][j][K] + sum[j] - sum[i - 1]);
            }
        }
        return dp[1][n][1];
    }
};

思路:

原始的石子合併問題是兩個兩個合併,此題多加了一個條件K,要求一次合併K堆石子。

石子合併問題可以用dp[i][j]dp[i][j]表示區間i到j這些石子合併後得到的最小決策值,如果是兩個石子合併,那麼在決策狀態中,如果是在第k個位置尋找子區間。那麼
dp[i][k]+dp[k+1][j]+fun(i,j,k)dp[i][k]+dp[k+1][j]+fun(i,j,k)表示區間i到j石子兩兩合併所做的決策狀態。

如果是三個石子呢?設置dp[i][j]dp[i][j]表示區間i到j之間三個石子三個石子合併得到的最優解。
同理可得
dp[i][k1]+dp[k1+1][k2]+dp[k2+1][j]+fun(i,j,k1,k2)dp[i][k_1]+dp[k_1+1][k_2]+dp[k_2+1][j]+fun(i,j,k_1,k_2)
這裏需要枚舉k1k_1k2k_2來尋找最優解

可想而知,如果是K很大的話,基本上沒有辦法做到對狀態劃分的枚舉。

這裏增加維度來表示區間i到j之間的狀態,題目中限定了合併石子個數K爲變量。
那麼很容易想到設定狀態dp[i][j][k]dp[i][j][k]來表示一個區間的石子合併後得到的最優值。

如果將上面的狀態解釋爲在區間i到區間j使用連續k個石子合併規則得到的最優值會有一個問題,如何建立狀態轉移呢?即,如何從兩個石子合併的狀態得到三個石子合併的狀態呢?很顯然做不到。

換一種想法考慮
設置dp[i][j][k]dp[i][j][k]表示爲將區間i到j的石子合併爲k堆的最小代價
那麼dp[i][j][k]=min(dp[i][x][k1]+dp[x+1][j][1])dp[i][j][k]=min(dp[i][x][k-1]+dp[x+1][j][1])即k-1堆石子與1堆石子合併得到k堆石子,可以實現遞推操作

合併的方式表現在狀態轉移的過程中,每次將得到的dp[i][j][K]dp[i][j][K]合併,得到將K堆石子合併後得到的最優值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章