acwing合併石子[區間dp]

石子合併
只能合併相鄰兩堆。求體力最小值
數據比較弱,最多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]

狀態轉移方程爲
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+sum[j]sum[i1])f[i][j]=min(f[i][j],f[i][k]+f[k+1][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]的最小體力
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章