一本通1274:【例9.18】合并石子(区间DP入门)

原题传送门

\(Thinking\)

这是一道区间\(DP\)经典/入门

状态的表示:\(f[i][j]\) 表示合并从 \(i\)\(j\) 堆的最小代价

初始化:\(f[i][i]\ =\ 0\)

状态的转移:\(f[i][j]=min\{f[i][j],f[i][k]+f[k+1][j]+sum[j]-sum[i-1]\}\)

解释:可以把 \([i,j]\) 取一个 \(k\) 值,分成两个更小的子区间,不断分解,即 \(f[i][j]\ \to\ f[i][k]+f[k+1][j]\)
而在合并的过程中,需要付出代价,而付出的代价就是这两个端点之间的前缀和

\(Code\)

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
inline void read(int &x){
	int f=1;
	char ch=getchar();
	x=0;
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
	x*=f;
}
int N;
int a[110];
int sum[110];
int f[110][110];
int main(){
	memset(f,0x3f,sizeof(f));	//求最小值初始化无穷大 
	read(N);
	for(int i=1;i<=N;i++){
		read(a[i]);
		sum[i]=sum[i-1]+a[i];	//sum[]处理前缀和 
		f[i][i]=0;	//初始化 
	}
	for(int i=N;i>=1;i--){	//倒序合并 
		for(int j=i+1;j<=N;j++){	//选一个在i右侧的堆 
			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]);
			}
		}
	}
	printf("%d",f[1][N]);	//[1,n] 
	return 0;
}
posted @ 2020-10-03 20:33  _pwl  阅读(1052)  评论(0)    收藏  举报
1 2 3
4