很明显的一个转移方程是: dp[i] = max(dp[j] + max(a[j+1], a[j+2], ..., a[i])); 其中满足 sum[i] - sum[j] <= M 的 j 都算,这是一个 O(N * N) 的解法,肯定超时。,那么就要用优化的方法 ,对着题解看了一个小时终于看懂了。
f[j]表示前j个数分为若干份,最优解。
g[i]表示使f[i]取得最优解的j,即最后一份为从g[i]分到i。
把第i+1个数加入进来。
如果超过了m,则把前面的数减去,直至小于m。
当总数小于m时:
如果a[i+1]小于mx(g[i]到i中的最大值)则可以忽略,f[i]=f[i-1],g[i]=g[i-1];
如果a[i+1]大于mx,则要确定最优值。(优化)因为f[i+1]的最优值不会再(g[i-1],i)内,所以可以直接从p到i+1计算。p为第p个数使p到i+1的总和小于m。但如果g[i-1]小于p,就要从i计算到p。
(g[i-1]>=p?g[i-1]:i);意思是如果之前的最大值不在当前字块内,那么就从i->p计算最大值之和的最小值;如果最大值还在的话,那么就只需要在计算g【i-1】== p处的最大值之和的最小值。其实就是更新一下f【i】和g【i】;如果f[i]>f[j-1]+mx的话,那么最优解肯定不是以g[i]为下标的那个解。那么更新f【i】=j;如果是f[i]<f[j-1]+mx,那么就不更新g[i]。
#include<iostream>
#include <cstdio>
using namespace std;
#define maxn 101010
typedef long long ll;
const ll inf=1000000000;
ll f[maxn];
int g[maxn];
ll a[maxn];
int n;
ll m;
int main()
{
int i,j;
while(scanf("%d%I64d",&n,&m)!=-1)
{
for(i=1; i<=n; i++)
{
scanf("%I64d",&a[i]);
if(a[i]>m)
{
printf("-1\n");
return 0;
}
}
f[1]=a[1];
g[1]=1;
int p=1;
f[0]=0;//因为当第一次更新的时候那个需要计算f[0]+mx,所以我们应该给f【0】赋值
ll mx=a[1],sum=a[1];
for(i=2; i<=n; i++)
{
int k=0;
f[i]=inf;
sum+=a[i];
while(sum>m)sum-=a[p++];
if(mx>=a[i]&&g[i-1]>=p)
{
f[i]=f[i-1];
g[i]=g[i-1];
}
else
{
mx=a[i];
k=(g[i-1]>=p?g[i-1]:i);
for(j=k; j>=p; j--)
{
mx=max(mx,a[j]);
g[i]=f[i]>mx+f[j-1]?j:g[i];
f[i]=min(f[i],f[j-1]+mx);
}
mx=f[i]-f[g[i]-1];
}
}
printf("%I64d\n",f[n]);
}
}
原文地址:http://blog.csdn.net/u013076044/article/details/45697415