51NOD 1053:最大M子段和 V2——题解
https://www.51nod.com/Challenge/Problem.html#problemId=1053
N个整数组成的序列a1,a2,a3,…,an,将这N个数划分为互不相交的M个子段,并且这M个子段的和是最大的。如果M >= N个数中正数的个数,那么输出所有正数的和。例如:-2 11 -4 13 -5 6 -2,分为2段,11 -4 13一段,6一段,和为26。
WQS二分/带权二分裸题,显然函数为凸函数,问题只需要解决没有限制的情况下如何求最大值。
f[i]为到i的最大值,则不取i就是f[i]=f[i-1],取i为f[i]=max{f[k]+sum[i]-sum[k]}-分一段代价。
显然max{f[k]-sum[k]}可以用dp求。
细节还是蛮多的,毕竟你还得记录求出最优值时分段个数,我的做法是限制在可能的情况下取更少的分段数,这样的情况下如果还>m就肯定不符合条件。
#include<queue> #include<cmath> #include<cstdio> #include<cctype> #include<cstring> #include<iostream> #include<algorithm> using namespace std; typedef long long ll; typedef long double dl; const int N=50005; inline int read(){ int X=0,w=0;char ch=0; while(!isdigit(ch)){w|=ch=='-';ch=getchar();} while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; } int n,m,pcnt,g[N],maxfi[N]; ll s[N],f[N],maxf[N],maxans; bool pan(ll c){ for(int i=1;i<=n;i++){ if(f[i-1]<maxf[i-1]+s[i]-c){ f[i]=maxf[i-1]+s[i]-c; g[i]=g[maxfi[i-1]]+1; }else{ f[i]=f[i-1]; g[i]=g[i-1]; } if(maxf[i-1]<f[i]-s[i]){ maxf[i]=f[i]-s[i]; maxfi[i]=i; } else{ maxf[i]=maxf[i-1]; maxfi[i]=maxfi[i-1]; if(maxf[i-1]==f[i]-s[i]){ if(g[i]>g[maxfi[i-1]]) maxfi[i]=maxfi[i-1]; else maxfi[i]=i; } } } return g[n]<=m; } ll solve(ll l,ll r){ ll b; while(l<r){ ll mid=(l+r)>>1; if(!pan(mid))l=mid+1; else{ b=f[n];r=mid; } } return l*m+b; } int main(){ n=read(),m=read(); for(int i=1;i<=n;i++){ ll a=read(); if(a>0){ ++pcnt;maxans+=a; } s[i]=s[i-1]+a; } if(pcnt<=m) printf("%lld\n",maxans); else printf("%lld\n",solve(0,1e9)); return 0; }
+++++++++++++++++++++++++++++++++++++++++++
+本文作者:luyouqi233。 +
+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/ +
+++++++++++++++++++++++++++++++++++++++++++