石子合併
只能合併相鄰兩堆。求體力最小值
數據比較弱,最多300堆,每堆重量不超過1000。
狀態表示
f[i][j]表示合併區間[i,j]需要的最小體力
狀態轉移
把區間[i,j]分成[i,k] 和[k+1,j]兩部分,由於這兩部分是獨立的,什麼意思?意思是合併左邊的體力和合並右邊的體力無關,所以要使得合併所有的石子體力最小,一定是兩堆體力都是最小值,現在我們回看 f[i,j]的定義,對於區間[i,k] 和[k+1,j],合併的體力消耗不就是f[i][k] 和 f[k+1][j]嘛
最後把[i,k] 和[k+1,j]這兩個區間的最終結果合併即可,這個結果是[i,j]中所有數據求和,可以用前綴和來優化 sum[j]-sum[i-1]
狀態轉移方程爲
ac代碼
#include<iostream>
#include<cstring>
#include<cstdlib>
using namespace std;
const int maxn=310;
int n;
int tmp;
int sum[maxn];
int f[maxn][maxn];//dp[i][j]表示區間[i,j]合併成爲一堆的最小體力
int main(){
cin>>n;
if(n==1){ cout<<0<<endl;exit(0);
}
sum[0]=0;
for(int i=1;i<=n;i++){
cin>>tmp,sum[i]=sum[i-1]+tmp;
}
memset(f,0,sizeof(f));
for(int len=2;len<=n;len++){//枚舉區間長度
for(int i=1;i+len-1<=n;i++){//枚舉區間左端點
int j=i+len-1;//區間右端點
f[i][j]=1e9;//假定[i,j]區間合併需要無窮大體力
for(int k=i;k<=j-1;k++)//枚舉中間點
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+sum[j]-sum[i-1]);
}
}
cout<<f[1][n]<<endl;//輸出區間[1,n]的最小體力
}