石子歸併————區間dp模板題

石子歸併:傳送門

 

N堆石子擺成一條線。現要將石子有次序地合併成一堆。規定每次只能選相鄰的2堆石子合併成新的一堆,並將新的一堆石子數記爲該次合併的代價。計算將N堆石子合併成一堆的最小代價。

 

例如: 1 2 3 4,有不少合併方法

1 2 3 4 => 3 3 4(3) => 6 4(9) => 10(19)

1 2 3 4 => 1 5 4(5) => 1 9(14) => 10(24)

1 2 3 4 => 1 2 7(7) => 3 7(10) => 10(20)

 

括號裏面爲總代價可以看出,第一種方法的代價最低,現在給出n堆石子的數量,計算最小合併代價。

Input

第1行:N(2 <= N <= 100) 第2 - N + 1:N堆石子的數量(1 <= Ai

<= 10000)

Output

輸出最小合併代價

Sample Input

4
1
2
3
4

Sample Output

19

 

思路:

區間dp模板題

注意dp數組初值,對於每個石子dp[i][i]=0, 其他設爲無限大

模板:

//w[i][j]表示從i 到 j的花費
for(i = 1;i <= n;i++)
    dp[i][i] = 初始值;
for(len = 2;len <= n;len++){//len選擇區間長度
    for(i = 1;i <= n;i++){//枚舉起點
        j = i + len - 1;//合併終點
        if(j > n)break;//不可越界
        for(k = i;k < j;k++)//枚舉分割點,尋找最優分割
            dp[i][j] = max(dp[i][j], dp[i][k] + dp[k + 1][j] + w[i][j]);//狀態轉移
    }
}

沒有優化:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>

#define MAXN 105
#define INF 0x3f3f3f3f

using namespace std;

int main()
{
    int n;
    scanf("%d", &n);
    int i, j, k, len, a[MAXN], dp[MAXN][MAXN], sum[MAXN];
    memset(dp, INF, sizeof(dp));
    memset(sum, 0, sizeof(sum));
    for(i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        dp[i][i] = 0;
        sum[i] = sum[i-1] + a[i];
    }
    for(len = 2; len <= n; len++)
    {
        for(i = 1; i <= n; i++)
        {
            int j = i+len-1;
            if(j > n) break;
            for(k = i; k < j; k++)
            {
                dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1]);
            }
        }
    }
    printf("%d\n", dp[1][n]);

    return 0;
}

四邊形優化:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>

#define MAXN 105
#define INF 0x3f3f3f3f

using namespace std;

int main()
{
    int n;
    scanf("%d", &n);
    int i, j, k, len, a[MAXN], dp[MAXN][MAXN], sum[MAXN], s[MAXN][MAXN];
    memset(dp, INF, sizeof(dp));
    memset(sum, 0, sizeof(sum));
    for(i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        s[i][i] = i;
        dp[i][i] = 0;
        sum[i] = sum[i-1] + a[i];
    }
    for(len = 2; len <= n; len++)
    {
        for(i = 1; i <= n; i++)
        {
            int j = i+len-1;
            if(j > n) break;
            for(k = s[i][j-1]; k <= s[i+1][j]; k++)
            {
                if(dp[i][j] > dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1])
                {
                    dp[i][j] = dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1];
                    s[i][j] = k;
                }
            }
        }
    }
    printf("%d\n", dp[1][n]);

    return 0;
}

 

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