51NOD 1053:最大M子段和 V2——題解
阿新 • • 發佈:2021-07-07
不同於其他部落格的做法(大概?),WQS二分/DP凸優化/帶權二分
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 longll; 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/+
+++++++++++++++++++++++++++++++++++++++++++