51nod 最大M子段和v1/v2/v3
\(N\) 个整数组成的序列 \(a[1],a[2],a[3],…,a[n]\),将这N个数划分为互不相交的 \(M\) 个子段,并且这 \(M\) 个子段的和是最大的。如果 \(M >= N\) 个数中正数的个数,那么输出所有正数的和。\(N,M<=5000\)。
例如:\(-2 11 -4 13 -5 6 -2\),分为 \(2\) 段,\(11 -4 13\) 一段,\(6\) 一段,和为 \(26\)。
V2
\(N\) 个整数组成的序列 \(a[1],a[2],a[3],…,a[n]\),将这 \(N\) 个数划分为互不相交的 \(M\) 个子段,并且这 \(M\) 个子段的和是最大的。如果 \(M >= N\) 个数中正数的个数,那么输出所有正数的和。 \(N,M<=50000\)。
例如:\(-2 11 -4 13 -5 6 -2\),分为 \(2\) 段,\(11 -4 13\) 一段,\(6\) 一段,和为 \(26\)。
最大M子段和 V3
环形最大 \(M\) 子段和,\(N\) 个整数组成的序列排成一个环,\(a[1],a[2],a[3],…,a[n]\)( \(a[n], a[1]\) 也可以算作1段),将这 \(N\) 个数划分为互不相交的 \(M\) 个子段,并且这 \(M\) 个子段的和是最大的。如果 \(M>=N\) 个数中正数的个数,那么输出所有正数的和。\(N,M<=100000\)。
例如:\(-2 11 -4 13 -5 6 -1\),分为 \(2\) 段,\(6 -1 -2 11\) 一段,\(13\) 一段,和为 \(27\)。
v2 是 v1 的升级版,而v3是v2的特殊版(更好处理了)所以我们先讲v2,我们将连续的正数和连续的负数都连成一段,对每个段用链表储存,对每一段的值用优先队列储存绝对值,对于绝对值小的段考虑将他两边的段合并,更新值。
看代码好理解,文字真的没啥意思。
#include<bits/stdc++.h> using namespace std; #define ll long long const int N=5e4+5; int n,m; int a[N];//原值 int k;//总段数 int p;//正数段数 ll s;//正数和 ll ans;//总答案 ll c[N]; int l[N],r[N];//左右的段的编号 int tra[N];//标记是否被合并 struct cmp{ bool operator()(const int x,const int y){ return abs(c[x])>abs(c[y]);//绝对值排序 } }; priority_queue<int,vector<int>,cmp> q; int main(){ ios::sync_with_stdio(false); cin>>n>>m; for(int i=1;i<=n;i++){ cin>>a[i]; } for(int i=1;i<=n;i++){ if((ll)a[i]*a[i-1]<0){//异号 if(s>0){//是正数要储存答案 ans+=s;//先求正数总和 ++p;//正数段数增加 } c[++k]=s;//开新段 s=a[i];//段正常增加 } else{ s+=a[i];//同号增加增加 } } if(s>0){//因为边界 ans+=s;//累加 p++;//正数段数增加 } if(s){ c[++k]=s;//非零新开一段 } //链表操作 r[1]=2; q.push(1); for(int i=2;i<k;i++){ l[i]=i-1; r[i]=i+1; q.push(i); } l[k]=k-1; q.push(k); int le=p-m;//需要合并几个段 if(le<=0){//已经可以输出了 cout<<ans; return 0; } int now; while(1){ now=q.top(); q.pop(); if(tra[now]){ continue; } if(c[now]<=0&&(!l[now]||!r[now])){//负数且在最左和最右 continue; } ans-=abs(c[now]);//合并了减去 c[now]+=c[l[now]]+c[r[now]];//合并值 if(l[now]){//能左合并 tra[l[now]]=1; l[now]=l[l[now]]; r[l[now]]=now; } if(r[now]){//能右合并 tra[r[now]]=1; r[now]=r[r[now]]; l[r[now]]=now; } q.push(now); le--;//减少合并次数 if(!le){ break; } } cout<<ans; return 0; }
看懂上面了,v3 直接就知道怎么操作,无非就是链表上进行操作。
#include<bits/stdc++.h> using namespace std; #define ll long long const int N=1e5+5; int n,m; int a[N]; int k; int p; ll s; ll ans; ll c[N]; int l[N],r[N]; int tra[N]; struct cmp{ bool operator()(const int x,const int y){ return abs(c[x])>abs(c[y]); } }; priority_queue<int,vector<int>,cmp> q; int main(){ ios::sync_with_stdio(false); cin>>n>>m; for(int i=1;i<=n;i++){ cin>>a[i]; } for(int i=1;i<=n;i++){ if((ll)a[i]*a[i-1]<0){ if(s>0){ ans+=s; ++p; } c[++k]=s; s=a[i]; } else{ s+=a[i]; } } if(s>0){ ans+=s; if(c[1]<0){//无法首尾合并 ++p; } } if(s){ if(c[1]*s<0){//无法首尾合并 c[++k]=s; } else{ c[1]+=s;//能合并 } } //这注意改 l[1]=k; r[1]=2; q.push(1); for(int i=2;i<k;i++){ l[i]=i-1; r[i]=i+1; q.push(i); } l[k]=k-1; r[k]=1; q.push(k); int le=p-m; if(le<=0){ cout<<ans; return 0; } int now; while(1){ now=q.top(); q.pop(); if(tra[now]){ continue; } if(c[now]<=0&&(!l[now]||!r[now])){ continue; } ans-=abs(c[now]); c[now]+=c[l[now]]+c[r[now]]; //直接合并 tra[l[now]]=1; l[now]=l[l[now]]; r[l[now]]=now; tra[r[now]]=1; r[now]=r[r[now]]; l[r[now]]=now; q.push(now); le--; if(!le){ break; } } cout<<ans; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」