链接:https://vjudge.net/problem/HihoCoder-1636
思路:区间dp,可以说是石子合并的加强版,只是因为由相邻合并改为了一个范围合并,所以我们要在原来dp上多加一维,dp[l][r][k]表示从l到r区间被分成k堆所需要的最小值,k=1时表示整个区间合并完成,状态表示如下:
k>2 dp[l][r][k] = min(dp[l][r][k],dp[l][d][1]+dp[d+1][r][k-1])
k=1 dp[l][r][1] = min(dp[l][r][1],dp[l][r][k]+sum(r)-sum(l-1))
巧妙的地方就是把答案放到k=1的地方去,这样我们只用在合并的时候加上前缀和,也算是一个套路吧,分解区间的时候不加前缀和,因为之前的都已经算好了,合并的时候更新当前状态的最优解,加上前缀和。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,L,R;
const int maxn = 500;
const ll INF = 1e18;
ll dp[maxn][maxn][maxn];
ll sum[maxn];
int a[maxn];
int main(){
while(~scanf("%d%d%d",&n,&L,&R)){
sum[0] = 0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
sum[i] = sum[i-1] + a[i];
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
dp[i][j][k] = INF;
}
}
}
for(int i=0;i<=n;i++)dp[i][i][1] = 0;
for(int len=2;len<=n;len++){
for(int l=1;l+len-1<=n;l++){
int r = l+len-1;
for(int p=2;p<=len;p++){//枚举大于1的情况
for(int k=l;k<r;k++){//拆分区间
dp[l][r][p] = min(dp[l][r][p],dp[l][k][1]+dp[k+1][r][p-1]);
}
}
//将最优解合并到k=1的时候,更新当前状态的最优解
for(int k=0;k<=n;k++){
if((k>=L&&k<=R)||(k==0&&len>=L&&len<=R))
dp[l][r][1] = min(dp[l][r][1],dp[l][r][k]+sum[r]-sum[l-1]);
}
}
}
printf("%lld\n",dp[1][n][1]>=INF?0:dp[1][n][1]);
}
return 0;
}