Leetcode1000 合並石頭的最低成本 區間DP


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

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

找出把所有石頭合並成一堆的最低成本。如果不可能,返回 -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,這是可能的最小值。
輸入:stones = [3,2,4,1], K = 3
輸出:-1
解釋:任何合並操作后,都會剩下 2 堆,我們無法再進行合並。所以這項任務是不可能完成的。.
輸入: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

當K=2時,每次合並都是相鄰的兩堆進行合並。用dp[i][j]表示從i到j這個區間合並為1個堆時的最小代價。那么有轉移方程:

dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1])

 //dp是從兩兩合並開始的,也就是說是長度先為2,然后3,....所以要枚舉len的長度
        //dp[i][j],len=j-i
        for(int len=1;len<n;len++)
        {
            for(int i=1;i<=n-len;i++)
            {
                int j=i+len;
                for(int k=i;k<j;k++)
                {
                    dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
                }
            }
        }

通過觀察上面的例子,我們可以知道K=2時,我們做的其實是將一部分合並為1堆,另一部分合並為1堆,最后形成一堆。典型的子問題划分問題,可以想象歸並排序的過程。

現在K!=2了,那么我們就將一部分合並成K-1堆,另一部分合並成1堆,然后合並。

至於為什么不是K-2堆和2堆以及K-3堆和3堆是因為我們的子問題是合並成1堆,當前狀態是由前一狀態得到的。而我們最初只有dp[i][i][1]=0這個條件

現在K可以為任意值,由樣例我們可以得到如果(n-1)%(k-1)不等於0的話,說明最終無法形成一堆。

現在考慮正常的情況,我們用dp[i][j][m]表示從i到j這個區間形成m堆所需的最小代價

初始化 dp[i][i][1]=0

dp[i][j][K]=min(dp[i][j][k],dp[i][k][K-1]+dp[k+1][j][1])

dp[i][j][1]=min(dp[i][j][K]+sum[j]-sum[k-1])

for (len=2;len<=n;++len){
        for (l=1;l+len-1<=n;++l)
        {
            r=l+len-1;
            for (k=l;k<r;++k)
            {
                for (i=2;i<=len;++i)
                {
                    f[l][r][i]=min(f[l][r][i],f[l][k][i-1]+f[k+1][r][1]);
                }
            }
            f[l][r][1]=min(f[l][r][K]+sum[r]-sum[l-1],f[l][r][1]);

        }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM