题意:
给你n堆石头,l,r,每次可以合并区间长度[l,r]的石子,合并代价为石子数目和
问你最后能否合并成一堆 0 : 合并代价
思路:
计算区间石子和的话先求个前缀和方便后面用
dp[i][j][k]——区间[i,j]合并成k堆的花费 dp初始化inf
对于区间长度len 有1 ~ n,可知对于每个len的[i,j] 初始dp[i][j][len] = 0
对于每个len 枚举k:2~len 对于每个k将[i,j]拆成两部分 dp[i][j][k] = min(dp[i][j][k],dp[i][L][1] + dp[L + 1][j][k - 1]);
当k的区间范围属于允许合并成一堆的范畴 dp[i][j][1] = min(dp[i][j][1],dp[i][j][k] + sum[j] - sum[l - 1]);
dp[1][n][1]就是答案
AC代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int MaxN = 102;
const int inf = 0x3f3f3f3f;
int n,l,r;
int a[MaxN],sum[MaxN];
int dp[MaxN][MaxN][MaxN];
int main()
{
while(~scanf("%d %d %d",&n,&l,&r)){
for(int i = 1;i <= n; i++) scanf("%d",&a[i]);
for(int i = 1;i <= n; 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 len = 1;len <= n; len++){
for(int i = 1;i + len - 1 <= n; i++){
int j = i + len - 1;
dp[i][j][len] = 0;
for(int k = 2;k <= len; k++){
for(int L = i;L <= j; L++){
dp[i][j][k] = min(dp[i][j][k],dp[i][L][1] + dp[L + 1][j][k - 1]);
}
if(k <= r && k >= l) dp[i][j][1] = min(dp[i][j][1],dp[i][j][k] + sum[j] - sum[i - 1]);
}
}
}
if(dp[1][n][1] >= inf) printf("0\n");
else printf("%d\n",dp[1][n][1]);
}
}