1. 程式人生 > 其它 >51NOD 1053:最大M子段和 V2——題解

51NOD 1053:最大M子段和 V2——題解

不同於其他部落格的做法(大概?),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 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/+

+++++++++++++++++++++++++++++++++++++++++++