Bzoj 2288 生日礼物题解
Posted Hzoi_joker
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Bzoj 2288 生日礼物题解相关的知识,希望对你有一定的参考价值。
2288: 【POJ Challenge】生日礼物
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 856 Solved: 260
[Submit][Status][Discuss]
Description
ftiasch 18岁生日的时候,lqp18_31给她看了一个神奇的序列 A1, A2, ..., AN. 她被允许选择不超过 M 个连续的部分作为自己的生日礼物。
自然地,ftiasch想要知道选择元素之和的最大值。你能帮助她吗?
Input
第1行,两个整数 N (1 ≤ N ≤ 105) 和 M (0 ≤ M ≤ 105), 序列的长度和可以选择的部分。
第2行, N 个整数 A1, A2, ..., AN (0 ≤ |Ai| ≤ 104), 序列。
Output
一个整数,最大的和。
Sample Input
5 2
2 -3 2 -1 2
Sample Output
HINT
Source
这道题当时第一眼以为连续的m个数,然后以为是二分加上单调队列,还好又读了一遍,发现是m个区间,这就比较有意思了,首先,我们可以先把所有符号相同的区间合在一起,来简化一下问题,然后这道题就变成了在一个正数与负数相间的数列中找出m个数使他们的和最大,但是我们要注意一点,这些数其实还可以合并成一个数,所以我们不能拿贪心去做。所以我们可以把答案分为两种情况,第一,所有正数的个数小于等于m那么我们可以直接输出了,第二正数的个数大于m,那么我们就有又有两种选择了,舍去某个正数或通过一个负数将两个区间合并在一起。对于位于首尾两个位置的负数我们可以的知他们并无法连接两个正数,换言之,选他们并不能减少当前所选的区间,所以我们在一开始就应当把他们刨去。同时我们也可以发现如果我们选了一个负数那么两侧的正数是不能再被选择丢掉的,而整个序列又是正负相间的,所以我们就可以将这个问题转化为在当前数列中选取若干个不相邻的数数是他们的和最小。然后,这道题就是Bzoj1150数据备份了。
1 #include<iostream> 2 #include<cstdlib> 3 #include<cstdio> 4 #include<cstring> 5 #include<queue> 6 #include<algorithm> 7 #include<cmath> 8 #include<map> 9 #include<vector> 10 #define N 100005 11 using namespace std; 12 int n,m,a[N],zz; 13 struct no 14 { 15 long long sum; 16 int mid; 17 bool friend operator > (no a,no b) 18 { 19 return a.sum>b.sum; 20 } 21 }node[N]; 22 int pre[N],fro[N]; 23 bool fw[N]; 24 priority_queue<no,vector<no>,greater<no > > q1; 25 int main() 26 { 27 scanf("%d%d",&n,&m); 28 for(int i=1;i<=n;i++) 29 scanf("%d",&a[i]); 30 long long sm=0,ans=0,js=0,la=0; 31 for(int i=1;i<=n;i++) 32 { 33 if(a[i]==0)continue; 34 if(a[i]*la<0) 35 { 36 zz++; 37 node[zz].sum=sm; 38 if(sm>0) 39 { 40 js++; 41 ans+=sm; 42 } 43 sm=0; 44 if(zz==1&&la<0)zz=0; 45 } 46 la=a[i]; 47 sm+=a[i]; 48 } 49 if(sm>0) 50 { 51 js++,ans+=sm; 52 zz++; 53 node[zz].sum=sm; 54 } 55 if(js<=m) 56 { 57 printf("%lld\n",ans); 58 exit(0); 59 } 60 m=js-m; 61 for(int i=1;i<=zz;i++) 62 { 63 node[i].mid=i; 64 node[i].sum=abs(node[i].sum); 65 q1.push(node[i]); 66 pre[i]=i-1,fro[i]=i+1; 67 } 68 fro[zz]=0; 69 node[0].sum=0x7fffffff; 70 while(m--) 71 { 72 while(fw[q1.top().mid]) q1.pop(); 73 no tt=q1.top();q1.pop(); 74 int x=tt.mid; 75 ans-=tt.sum; 76 tt.sum=-tt.sum; 77 tt.sum+=node[pre[x]].sum+node[fro[x]].sum; 78 node[x].sum=tt.sum; 79 80 fw[pre[x]]=fw[fro[x]]=1; 81 pre[x]=pre[pre[x]]; 82 fro[x]=fro[fro[x]]; 83 pre[fro[x]]=x; 84 fro[pre[x]]=x; 85 q1.push(tt); 86 } 87 printf("%lld\n",ans); 88 return 0; 89 }
以上是关于Bzoj 2288 生日礼物题解的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ 2288 POJ Challenge生日礼物(贪心+优先队列)
BZOJ3502/2288PA2012 Tanie linie/POJ Challenge生日礼物 堆+链表(模拟费用流)