当前位置: 代码迷 >> 综合 >> set+链表 【POJ Challenge】生日礼物
  详细解决方案

set+链表 【POJ Challenge】生日礼物

热度:18   发布时间:2023-12-08 14:48:21.0

2288: 【POJ Challenge】生日礼物

Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 841 Solved: 255
[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
5

可以发现,连续的一段同号整数一定被一起选上.所以把连续的整数合并,去掉0.
先选上所有的正数段,如果段数<=m,直接输出,如果大于,就需要添上一段负数使两个正数段连在一起,或者去掉一段正数。所以把所有数改成正数,用set选最小的。
而且不能选连续的两段,如果左右端点是负数,选他没必要,不能选。
具体怎么搞,见这个题解(思路相同)就

#pragma GCC optimize("O3")
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<set>
#include<cmath>
#define N 100005
#define ll long long
using namespace std;
int n,m,num,tot;
int fro[N],nex[N];
struct node
{int id;ll h;node(){}node(int x,ll y){id=x;h=y;}inline friend bool operator <(const node &x,const node &y){
   return (x.h!=y.h)?x.h<y.h:x.id<y.id;}
};set<node> s;
ll a[N],ans;
int main()
{scanf("%d%d",&n,&m);ll x;for(int i=1;i<=n;i++){scanf("%lld",&x);if(x==0)continue;if(tot==0){a[++tot]=x;continue;}if((x>0&&a[tot]>0)||(x<0&&a[tot]<0))a[tot]+=x;else a[++tot]=x;}for(int i=1;i<=tot;i++)if(a[i]>0){num++;ans+=a[i];}if(num<=m){
   cout<<ans;return 0;}m=num-m;int p=1;if(a[p]<0)p++;if(a[tot]<0)tot--;for(int i=p;i<=tot;i++){if(a[i]<0)a[i]=-a[i];fro[i]=i-1;nex[i]=i+1;s.insert(node(i,a[i]));}fro[p]=0;nex[0]=p;nex[tot]=0;a[0]=10000000000000ll;while(m--){int x=s.begin()->id;ans-=a[x];a[x]=-a[x];a[x]+=a[fro[x]];a[x]+=a[nex[x]];s.erase(s.begin());s.erase(node(fro[x],a[fro[x]]));s.erase(node(nex[x],a[nex[x]]));s.insert(node(x,a[x]));if(fro[fro[x]])nex[fro[fro[x]]]=x;if(nex[nex[x]])fro[nex[nex[x]]]=x;fro[x]=fro[fro[x]];nex[x]=nex[nex[x]];}cout<<ans;
}
  相关解决方案