UPC-5843: 摘樱桃(最优状态递推)
很难想到的最优状态递推,一开始总在如何分组的问题上纠结。其实只需要在分配第i个樱桃时,将其与前一个(j=i-1)分为一组、前两个分为一组(j=i-2),前三个分为一组(j=i-3)。。。。然后取剩下j个的最优分配情况dp[j]求和取最小值即可。
dp【j】表示了当有j个樱桃时的最优解分配。(是做了-T且平方处理的最终结果)
用一个前缀和数组sum存储前N项前缀和,通过作差可以得到i到j区间的总和,遍历i到j不断取区间和当做使当前第i个樱桃与之前j个分为一组,然后再次基础上-T并平方,与第j-1个樱桃时的最优解进行求和,得到第i个时的最优解的临时值,直到遍历完成,取合并后的最小值即当前i个的最优解。
代码如下:
#include<stdio.h>///类似最长递增子序列的递推,不用考虑分组问题,不断对其递推最优解即可
#include<math.h>
#include<algorithm>
#include<string.h>
using namespace std;
int n,t,a[1005],dp[1005],sum[1005];
int main()
{
while(scanf("%d%d",&n,&t)!=EOF)
{
memset(dp,0x3f,sizeof(dp));///因为求最小值,因此初始化为最大值
for(int i=1; i<=n; i++) scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];///前缀和计算
dp[1]=(a[1]-t)*(a[1]-t);///只有一个樱桃时只有一种解
dp[0]=0;
for(int i=2; i<=n; i++)///有i个樱桃时的最优解
for(int j=i-1; j>=0; j--)///第i个樱桃与j个樱桃合为一组,再加上剩余樱桃的最优解,不断遍历合并取最小值
dp[i]=min(dp[i],(sum[i]-sum[j]-t)*(sum[i]-sum[j]-t)+dp[j]);///当前dp【i】的解,和其他分配方案的解,取最优解
printf("%d\n",dp[n]);
}
}