一本通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;
}
本文欢迎转载,转载时请注明本文链接

浙公网安备 33010602011771号