51nod1053&&1052 最大M子段和

题面

我们先将所有连续正段和连续负段合并,那么选取所有正段一定是最优的,但是选取的段数有可能超过$m$段,这时我们就需要合并。

1.选取不在两端的一个负段与它两边正段合并,块数减少$1$,子段和减少$|v[i]|$。

2.选取一个正段,将其删除,并与其左右负段合并,块数减少$1$,子段和减少$|v[i]|$。

所以我们将每段按照$|v[i]|$大小排序,每次贪心合并至段数小于等于$m$。

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <queue>
 4 #include <algorithm>
 5 using namespace std;
 6 #define pa pair<int,int>
 7 #define maxn 100001
 8 #define INF 0x3f3f3f3f
 9 inline int read()
10 {
11     int s=0,f=1;
12     char ch=getchar();
13     while(ch<‘0‘||ch>‘9‘)
14     {
15         if(ch==‘-‘)
16             f=-1;
17         ch=getchar();
18     }
19     while(ch>=‘0‘&&ch<=‘9‘)
20         s=(s<<1)+(s<<3)+ch-‘0‘,ch=getchar();
21     return s*f;
22 }
23 int pr[maxn],nx[maxn],s[maxn];
24 int n,k,ans;
25 priority_queue<pa,vector<pa>,greater<pa> >q;
26 int main()
27 {
28     n=read();
29     k=read();
30     int x,y;
31     for(int i=1;i<=n;i++)
32     {
33         x=read();
34         s[i]=x-y;
35         y=x;
36         if(i!=1)
37             q.push(pa(s[i],i));
38         pr[i]=i-1;
39         nx[i]=i+1;
40     }
41     pr[2]=0;
42     nx[n]=0;
43     while(k--)
44     {
45         while(q.top().first!=s[q.top().second])
46             q.pop();
47         int u=q.top().second;
48         int l=pr[u],r=nx[u];
49         q.pop();
50         ans+=s[u];
51         s[u]=l&&r?s[l]+s[r]-s[u]:INF;
52         pr[nx[u]=nx[r]]=u;
53         nx[pr[u]=pr[l]]=u;
54         s[l]=s[r]=INF;
55         q.push(pa(s[u],u));
56     }
57     printf("%d",ans);
58 }

51nod 1052&&1053

时间: 2024-08-18 20:12:45

51nod1053&&1052 最大M子段和的相关文章

1052 最大M子段和

1052 最大M子段和 N个整数组成的序列a[1],a[2],a[3],…,a[n],将这N个数划分为互不相交的M个子段,并且这M个子段的和是最大的.如果M >= N个数中正数的个数,那么输出所有正数的和. 例如:-2 11 -4 13 -5 6 -2,分为2段,11 -4 13一段,6一段,和为26. Input 第1行:2个数N和M,中间用空格分隔.N为整数的个数,M为划分为多少段.(2 <= N , M <= 5000) 第2 - N+1行:N个整数 (-10^9 <= a[

51nod 1052最大M子段和 &amp; poj 2479最大两子段和

最大子段和经典问题的扩展. 题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1052 N个整数组成的序列a[1],a[2],a[3],…,a[n],将这N个数划分为互不相交的M个子段,并且这M个子段的和是最大的.如果M >= N个数中正数的个数,那么输出所有正数的和. 例如:-2 11 -4 13 -5 6 -2,分为2段,11 -4 13一段,6一段,和为26. Input 第1行:2个数N和M,中间用空格分

51nod 1052 最大M子段和

N个整数组成的序列a[1],a[2],a[3],…,a[n],将这N个数划分为互不相交的M个子段,并且这M个子段的和是最大的.如果M >= N个数中正数的个数,那么输出所有正数的和. 例如:-2 11 -4 13 -5 6 -2,分为2段,11 -4 13一段,6一段,和为26. 收起 输入 第1行:2个数N和M,中间用空格分隔.N为整数的个数,M为划分为多少段.(2 <= N , M <= 5000) 第2 - N+1行:N个整数 (-10^9 <= a[i] <= 10^

51nod 1052 最大M子段和(动态规划)

分析:记dp[x][s][1]为从第x个数开始,剩余s段可以分,1表示x跟上一段连着,0表示不连着,递推式为dp[x][s][1]=max{dp[x+1][s][1]+a[x],dp[x+1][s][0]},dp[x][s][0]=max{dp[x+1][s-1][1]+a[x],dp[x+1][s][0]}. 1 #include<iostream> 2 #include<cstring> 3 using namespace std; 4 const int maxn=5005;

51nod1052 最大M子段和

dp优化我总是不太熟练.这一次首先我写了O(n4)->O(n3)->O(n2).一步步的优化过来.yyl好像用的是单调队列优化dp我看不懂他的代码... O(n4) #include<cstdio> #include<cstring> #include<cctype> #include<algorithm> using namespace std; #define rep(i,s,t) for(int i=s;i<=t;i++) #defi

51Nod1053 最大M子段和V2 二分+DP

传送门 直接DP的话最多也只能做到\(O(nm)\),对于\(5\times 10^4\)的数据范围实在无能为力 夹克老爷提供的做法是贪心,思想大概是在调整的同时,合理构造每个选择对应的新状态,使得新状态的一些选择可以代表"反悔"当前决策 (然而我没看懂--要是我看懂了也就不会有这个做法了) 其实还有另一种可能更好理解的做法 我们不妨考虑一种类似王钦石二分的思路 可以为每段额外加上一个相同的损失,在之后求最优解时不再考虑段数的限制 不难发现这个损失越大答案的段数就会越少,损失越小段数就

最大M子段和 V2

51nod1053 这题还是我们熟悉的M子段和,只不过N,M<=50000. 这题似乎是一个堆+链表的题目啊 开始考虑把所有正数负数锁在一起. 比如: 1 2 3 -1 –2 -3 666 缩成 6 -6 666这样. 然后用一个堆来维护,就是说把所有的负数和正数都扔进堆里,先选所有正数,然后每一次把堆中绝对值最小的数(如果是负数且没有左或右就跳过)和两边合并,链表维护一下. 当然实际实现用的是set- #include <iostream> #include <stdio.h&g

COGS 1052. [Vijos1022] Victoria的舞会2

1052. [Vijos1022] Victoria的舞会2 并查集吧... #include<bits/stdc++.h> using namespace std; #define maxn 1000000 #define maxm 2333 int n,m,ans,fa[maxn],x,a[maxm][maxm]; int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); } int main() { freopen("vict

最大子段和及其拓展

1.最大子段和问题 问题定义:对于给定序列a1,a2,a3--an,寻找它的某个连续子段,使得其和最大(如果某子序列全是负数则定义该子段和为 0).如( -2,11,-4,13,-5,-2 )最大子段是{ 11,-4,13 }其和为20. ·状态设计: dp[i] (1 <= i <= N) 表示以 a[i] 结尾的最大连续子段和. 显然,dp[i] >= 0 (1 <= i <= N) 状态转移方程:dp[i] = max{dp[i-1] + a[i], 0} (2 <