1. 程式人生 > >石子合併【區間dp】

石子合併【區間dp】

有N堆石子排成一排,每堆石子有一定的數量。現要將N堆石子併成為一堆。合併的過程只能每次將相鄰的兩堆石子堆成一堆,每次合併花費的代價為這兩堆石子的和,經過N-1次合併後成為一堆。求出總的代價最小值。

假設dp[1][4]表示將區間1~4的石子合併所花費的代價。dp[1][4]可以劃分為dp[1][1]+dp[2][4]、dp[1][2]+dp[3][4]、dp[1][3]+dp[4][4]。還可以往下繼續劃分。這是劃分的區間層次圖:


我們只需要用dp從下往上推就行了。我們可以用一個sum陣列來儲存一段區間內的合併代價。用k來表示分割點,嘗試區間內所有可能的分割,取代價最小的那個。

轉移方程:dp[begin][end]=dp[begin][k]+dp[k+1][end]+sum[end]-sum[begin-1]

;

程式碼:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define inf 1<<30
int dp[210][210],sum[210],a[210];
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        for(int i=1; i<=n; i++)
            scanf("%d",&a[i]);
        sum[0]=0;
        for(int i=1; i<=n; i++)
            sum[i]=sum[i-1]+a[i];
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=n; j++)
                if(i==j)
                    dp[i][j]=0;
                else
                    dp[i][j]=inf;
        }
        for(int i=1; i<n; i++)//合併的次數
        {
            for(int begin=1; begin+i<=n; begin++)
            {
                int end=i+begin;
                for(int k=begin; k<end; k++)
                {
                    if(dp[begin][end]>dp[begin][k]+dp[k+1][end]+sum[end]-sum[begin-1])
                        dp[begin][end]=dp[begin][k]+dp[k+1][end]+sum[end]-sum[begin-1];
                }
            }
        }
        printf("%d\n",dp[1][n]);
    }
    return 0;
}