最大K段和

题目大意

有一个长度为 N 的序列 A 。他希望从中选出不超过 K 个连续子段,满足它们两两不相交,求总和的最大值(可以一段也不选,答案为 0)。

分析

很容易想到 O(n2)dp
f[i][j] 表示选到第 i 位,已选了 j 段时的最大答案
那么 f[i][j]=max(f[i1][j]s[i]+max0<l<i(f[l][j1]s[l]))
然后维护最大的 f[l][j1]s[l]O(1) 更新即可
然后我们可以想到 WQS 二分(虽然我想不到)
它大概就是解决:有 n 个带权物品,用满足一定限制的方法选 m 个,使得其权值和取最值,而且权值和的最值是关于 m 的凸函数
注意 x 是段数
用直线 y=kx+b 去切
因为我们要求最大值,所以要最大化 b
b=ykx
那么我们就可以将原来的 dp 是改为
f[i]=max(f[i1]s[i]k+max0<l<i(f[l]s[l]))

总的来说,先二分 k,然后判断就 dp,并记录所分的段数
段数恰为 m 时就为答案
注意最后要以 k=ansdp 一遍

Code

#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
typedef long long LL;
const int N = 1e5 + 5;
int n , k , a[N];
LL f[N] , g[N] , s[N] , l , r , mid , ans;
bool check()
{
int x = 0;
for(register int i = 1; i <= n; i++)
{
f[i] = f[i - 1] , g[i] = g[i - 1];
if (f[x] + s[i] - s[x] - mid > f[i])
f[i] = f[x] + s[i] - s[x] - mid , g[i] = g[x] + 1;
if (f[i] - s[i] > f[x] - s[x]) x = i;
}
return g[n] >= k;
}
int main()
{
freopen("maxksum.in" , "r" , stdin);
freopen("maxksum.out" , "w" , stdout);
scanf("%d%d" , &n , &k);
for(register int i = 1; i <= n; i++)
scanf("%d" , &a[i]) , s[i] = s[i - 1] + a[i];
r = 0x3f3f3f3f3f3f3f3f;
while (l <= r)
{
mid = (l + r) >> 1;
if (check()) ans = mid , l = mid + 1;
else r = mid - 1;
}
mid = ans , check();
printf("%lld" , f[n] + ans * k);
}
posted @   leiyuanze  阅读(369)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示