题目链接:https://hihocoder.com/problemset/problem/1636
题意:给定n,l,r三个参数和一个长度为n的数组p,每次可以将长度为x(l<=x<=r)的连续区间合并为一个点,代价为该区间和值,该点的权值也为该区间和值,问将p数组合为一个点所需的最小代价和,若不能合并为一个点,则输出0
思路:区间dp,先遍历所有可能区间的长度,按照区间长度遍历所有区间,dp[i][j][k]表示从i到j区间合成k个堆的最小代价和,给其付初值均为inf,将dp[i][j][j-i+1]均赋为0,状态转移方程如下:
dp[i][i+d][1] = min(dp[i][i+d][1],dp[i][u][a]+dp[u+1][i+d][1]+sum[j] - sum[i-1]), l<=a+1<=r, i<=u<i+d
dp[i][i+d][k] = min(dp[i][i+d][k],dp[i][u][k-1]+dp[u+1][i+d][1]),1<k<=d+1
代码:
/*#include <bits/stdc++.h>*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const ll mod = 2147493647;
const int maxn = 105;
const double eps = 1e-8;
const ll inf = 10000000000000;
ll dp[maxn][maxn][maxn];
int n, l, r;
ll p[maxn];
ll sum[maxn];
int main()
{
while(~scanf("%d%d%d",&n,&l,&r))
{
for(int i=0;i<105;i++)
{
for(int j=i;j<105;j++)
{
for(int k=0;k<105;k++)
dp[i][j][k] = inf;
dp[i][j][j-i+1] = 0;
}
}
sum[0] = 0;
for(int i=1;i<=n;i++)
{
scanf("%lld",&p[i]);
sum[i] = sum[i-1]+p[i];
}
for(int d = 1;d<n;d++)
{
for(int i=1;i+d<=n;i++)
{
int j = i+d;
for(int u = i;u<j;u++)
{
for(int a = l-1;a<r;a++)
{
if(a>u-i+1)break;
if(dp[i][u][a]>=inf || dp[u+1][j][1]>=inf)continue;
dp[i][j][1] = min(dp[i][j][1],dp[i][u][a]+dp[u+1][j][1]+sum[j] - sum[i-1]);
}
for(int a = 1;a<=u-i+1;a++)
{
if(dp[i][u][a]>=inf || dp[u+1][j][1]>=inf)continue;
dp[i][j][a+1] = min(dp[i][j][a+1],dp[i][u][a]+dp[u+1][j][1]);
}
}
}
}
if(dp[1][n][1]>=inf)
printf("0\n");
else
printf("%lld\n",dp[1][n][1]);
}
}