[洛谷P1880][NOI1995]石子合併

區間DP模板題

區間DP模板Code:

for(int len=2;len<=n;len++)
    {
        for(int i=1;i<=2*n-1;i++)  //區間左端點 
        {
            int j = i + len - 1;  //區間右端點 
            for(int k=i;k<j;k++)  //斷點位置 
            {
                f[i][j] = min(f[i][j],f[i][k] + f[k + 1][j] + s[j] - s[i - 1]);
            }
        }
    }

 

題目描述

在一個園形操場的四周擺放N堆石子,現要將石子有次序地合併成一堆.規定每次只能選相鄰的2堆合併成新的一堆,並將新的一堆的石子數,記爲該次合併的得分。

試設計出1個算法,計算出將N堆石子合併成1堆的最小得分和最大得分.

輸入輸出格式

輸入格式:

數據的第1行試正整數N,1≤N≤100,表示有N堆石子.第2行有N個數,分別表示每堆石子的個數.

輸出格式:

輸出共2行,第1行爲最小得分,第2行爲最大得分.

輸入輸出樣例

輸入樣例#1: 4 4 5 9 4

輸出樣例#1: 43 54

這個題的數據存儲特點有一點代表性:破環成列,把長度爲n的環轉換爲長度爲2n-1的列,再進行一次動歸。

針對於這個題的n很小,我們就可以用它來代表區間長度,這樣O(n ^ 3)也能跑過去了

區間DP的概念就是把一個區間的狀態一直分割爲它的子區間的狀態,一直到這個子區間的狀態是顯然可求的,最後再將它們綜合起來

舉個栗子:

f[i][j]中我們可以將[i,j]這一個區間劃分爲[i,k]和[k + 1,j]這兩個區間的總狀態再進行一次操作

這個的邊界就是[i,k]和[k + 1,j]是顯然可求的狀態

這個題要求一個最大值和最小值的問題

我們可以顯而易見地發現最小值一定小於等於最大值

這樣我們可以只建立一個數組先求最小再求最大,節省了兩個數組的空間(雖然這不是重點qwq)

Code:

 

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring> 
using namespace std;
int f[300][300];  //節省空間 
int s[300];
int n,x,ans = 55312725;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        s[i] = s[i - 1] + x;
        s[i + n] = s[i];  //把長度開到 2n - 1 
    }
    for(int i=1;i<n;i++)
    s[i + n] += s[n];
    memset(f,10,sizeof(f));  //初始化 
    for(int i=1;i<=2*n-1;i++)
    f[i][i] = 0;
    for(int len=2;len<=n;len++)
    {
        for(int i=1;i<=2*n-1;i++)  //區間左端點 
        {
            int j = i + len - 1;  //區間右端點 
            for(int k=i;k<j;k++)  //斷點位置 
            {
                f[i][j] = min(f[i][j],f[i][k] + f[k + 1][j] + s[j] - s[i - 1]);
            }
        }
    }
    for(int i=1;i<=n;i++)
    ans = min(ans,f[i][i + n - 1]);
    printf("%d\n",ans);  //最小值一定比最大值要小,所以無需更新 
    for(int len=2;len<=n;len++)
    {
        for(int i=1;i<=2*n-1;i++)  //區間左端點 
        {
            int j = i + len - 1;  //區間右端點 
            for(int k=i;k<j;k++)  //斷點位置 
            {
                f[i][j] = max(f[i][j],f[i][k] + f[k + 1][j] + s[j] - s[i - 1]);
            }
        }
    }
    for(int i=1;i<=n;i++)
    ans = max(ans,f[i][i + n - 1]);
    printf("%d\n",ans);
    return 0; 
}

 

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