【BZOJ 2288】 2288: 【POJ Challenge】生日礼物 (贪心+优先队列+双向链表)

2288: 【POJ Challenge】生日礼物

Description

ftiasch 18岁生日的时候,lqp18_31给她看了一个神奇的序列 A1A2, ..., AN. 她被允许选择不超过 M 个连续的部分作为自己的生日礼物。

自然地,ftiasch想要知道选择元素之和的最大值。你能帮助她吗?

Input

第1行,两个整数 N (1 ≤ N ≤ 105) 和 M (0 ≤ M ≤ 105), 序列的长度和可以选择的部分。

第2行, N 个整数 A1A2, ..., AN (0 ≤ |Ai| ≤ 104), 序列。

Output

一个整数,最大的和。

Sample Input

5 2

2 -3 2 -1 2

Sample Output

5

HINT

Source

【分析】

  我好笨啊。。。

  首先可以把序列弄的好看点,0删掉,负数一段合并,正数一段合并。

  那么就是- + - + - 交替。

  假设我们先把所有正段选了。假设选了cnt段。

  如果cnt<=m,那么这显然就是答案。  

  否则,按照他们的绝对值扔进小根堆里面,每次选队顶元素。ans-=他的值。

  如果选到正数,表示把这个正数删掉,就少了一段。

  如果选到负数,表示把负数两边的两段连起来,也少了一段。

  知道这个意思之后就知道两端的负数是不可以选的,因为没有意义。

  然后删掉那个数之后和两边的两段合并起来重新扔到堆里面做。。【里面包含后悔操作!!!!思考一下!!!!

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<iostream>
 5 #include<algorithm>
 6 #include<queue>
 7 using namespace std;
 8 #define Maxn 100100
 9
10 struct node
11 {
12     int id,x;
13     friend bool operator < (node x,node y)
14     {
15         return x.x>y.x;
16     }
17 };
18
19 priority_queue<node > q;
20
21 int a[Maxn],w[2*Maxn];
22 int lt[2*Maxn],nt[2*Maxn];
23 bool mark[2*Maxn];
24
25 int myabs(int x) {return x<0?-x:x;}
26
27 int main()
28 {
29     int n,m;
30     scanf("%d%d",&n,&m);
31     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
32     w[1]=a[1];
33     int p=1;
34     for(int i=2;i<=n;i++)
35     {
36         if(a[i]==0) continue;
37         if((a[i]>=0&&w[p]>=0)||(a[i]<=0&&w[p]<=0)) w[p]+=a[i];
38         else w[++p]=a[i];
39     }
40     int cnt=0,ans=0;
41     for(int i=1;i<=p;i++) if(w[i]>0) cnt++,ans+=w[i];
42     if(cnt>m)
43     {
44         m=cnt-m;
45         memset(mark,0,sizeof(mark));
46         while(!q.empty()) q.pop();
47         for(int i=1;i<=p;i++) nt[i]=i+1;nt[p]=-1;
48         for(int i=1;i<=p;i++) lt[i]=i-1;
49         for(int i=1;i<=p;i++)
50         {
51             node xx;
52             xx.id=i;
53             xx.x=myabs(w[i]);
54             q.push(xx);
55         }
56         cnt=p;
57         for(int i=1;i<=m;i++)
58         {
59             while(mark[q.top().id]) q.pop();
60             node xx=q.top();q.pop();
61             if(lt[xx.id]==0)
62             {
63                 lt[nt[xx.id]]=0;
64                 if(w[xx.id]<0)
65                 {
66                     i--;
67                     continue;
68                 }
69                 ans-=xx.x;
70             }
71             else if(nt[xx.id]==-1)
72             {
73                 nt[lt[xx.id]]=-1;
74                 if(w[xx.id]<0)
75                 {
76                     i--;
77                     continue;
78                 }
79                 ans-=xx.x;
80             }
81             else
82             {
83                 ans-=xx.x;
84                 mark[lt[xx.id]]=mark[nt[xx.id]]=1;
85                 cnt++;
86                 nt[lt[lt[xx.id]]]=lt[nt[nt[xx.id]]]=cnt;
87                 lt[cnt]=lt[lt[xx.id]];nt[cnt]=nt[nt[xx.id]];
88                 node nw;
89                 nw.id=cnt;
90                 nw.x=myabs(w[nt[xx.id]]+w[lt[xx.id]]+w[xx.id]);
91                 w[cnt]=w[nt[xx.id]]+w[lt[xx.id]]+w[xx.id];
92                 q.push(nw);
93             }
94         }
95     }
96     printf("%d\n",ans);
97     return 0;
98 }

给一个sample

in:
5
53 -20 3 -27 68

out:
77

发发表情更健康

2017-01-14 11:01:57

时间: 2024-10-19 09:52:00

【BZOJ 2288】 2288: 【POJ Challenge】生日礼物 (贪心+优先队列+双向链表)的相关文章

POJ 1862 Stripies 贪心+优先队列

http://poj.org/problem?id=1862 题目大意: 有一种生物能两两合并,合并之前的重量分别为m1和m2,合并之后变为2*sqrt(m1*m2),现在给定n个这样的生物,求合并成一个的最小重量 思路: m1+m2 >=  2*sqrt(m1*m2) 所以每次取大的去合并,能变小. 直接优先队列就可以啦. #include<cstdio> #include<cmath> #include<queue> using namespace std;

BZOJ 2287 【POJ Challenge】消失之物

2287: [POJ Challenge]消失之物 Description ftiasch 有 N 个物品, 体积分别是 W1, W2, ..., WN. 由于她的疏忽, 第 i 个物品丢失了. “要使用剩下的 N - 1 物品装满容积为 x 的背包,有几种方法呢?” -- 这是经典的问题了.她把答案记为 Count(i, x) ,想要得到所有1 <= i <= N, 1 <= x <= M的 Count(i, x) 表格. Input 第1行:两个整数 N (1 ≤ N ≤ 2

BZOJ 2287: 【POJ Challenge】消失之物( 背包dp )

虽然A掉了但是时间感人啊.... f( x, k ) 表示使用前 x 种填满容量为 k 的背包的方案数, g( x , k ) 表示使用后 x 种填满容量为 k 的背包的方案数. 丢了第 i 个, 要填满容量为 k 的背包 , 则 ans( i , k ) = ∑ f( i - 1, h ) * g( i + 1 , k - h ) ( 0 <= h <= k ) 这样就转化为经典的背包问题了 f( x , k ) = f( x - 1 , k ) + f( x - 1 , k - w( x

BZOJ 2288 【POJ Challenge】生日礼物(贪心+优先队列)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2288 [题目大意] 给出一列数,求最多取m段连续的数字,使得总和最大 [题解] 首先我们对数据进行合并处理,连续的一段正数或者连续的一段负数处理成一个数字, 之后我们发现,如果正数的个数小于等于m,那么直接输出正数的总和即可, 如果大于m,我们有些正数不选,或者选择一些负数把左右两端的正数并起来. 这个负数的选择过程相当于减去这个数的绝对值, 正数选择拿出去的过程也相当于减去这个数的

bzoj 2288: 【POJ Challenge】生日礼物【链表+堆】

参考:http://blog.csdn.net/w_yqts/article/details/76037315 把相同符号的连续数字加起来,合并后ans先贪心的加上所有正数,如果正数个数sum>m,设计二元组(i,a[i])表示合并后序列i位置上值为a,记录前驱后继,塞进按绝对值排序的小根堆里.每次拿出来一个,减去这个x的a的绝对值,然后合并左右前驱后继,再塞回去. 如果拿出来的是正数,那么减去相当于原来选了现在不选,如果是负数,减去相当于选. 然后a值要改成a[l]+a[r]+a[x],下次再

poj -3614 Sunscreen(贪心 + 优先队列)

http://poj.org/problem?id=3614 有c头奶牛在沙滩上晒太阳,每头奶牛能忍受的阳光强度有一个最大值(max_spf) 和最小值(min_spf),奶牛有L种防晒霜,每种可以固定阳光强度在某一个值,每种的数量是cover[i] ,每头奶牛只能用一瓶防晒霜,问最多有多少头奶牛能在沙滩上晒太阳. 理解题意之后还是挺好做的. 首先确定的贪心策略是,在满足min_spf的条件下,尽量用spf小的用在max_spf大的奶牛身上,用一个最小堆维护max_spf的最小值即可. 先对奶牛

[bzoj2288][POJ Challenge]生日礼物

用堆维护双向链表来贪心... 数据范围显然不容许O(nm)的傻逼dp>_<..而且dp光是状态就n*m个了..显然没法优化 大概就会想到贪心乱搞了吧...一开始想贪心地通过几段小的负数把正数连接成一段,但到底是要连接在一起还是直接扔掉不好判断 然后就跑去翻题解了...题解讲的挺好的,连我都看懂了>_<..题解网址:http://www.cnblogs.com/tuigou/p/4868127.html 虽然选正数和负数的意义不同,但实际的操作都是把两边的数合并起来.还有就是,对于在

BZOJ 2287【POJ Challenge】消失之物

dp好厉害啊.....要多练才行. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 2050 using namespace std; int n,m,f[maxn][maxn],w[maxn],c[maxn][maxn]; int main() { scanf("%d%d",&n,&m); for

BZOJ 2296【POJ Challenge】随机种子(构造)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2296 [题目大意] 给出一个数x,求一个10的16次以内的数使得其被x整除并且数字包含0到9所有数字 [题解] 我们构造数前10位为0到9,利用后面六位补充使得其为x的倍数,x为10的6次方以内, 因此除x=0的特殊情况外一定存在解. [代码] #include <cstdio> using namespace std; int T,x; int main(){ scanf(&qu