首页 > 代码库 > 名校联赛DAY.2A层第二题SO(就)题解

名校联赛DAY.2A层第二题SO(就)题解

问题 B: 就

时间限制: 1 Sec  内存限制: 512 MB

题目描述

就so.in/.out

【背景描述】

一排 N 个数, 第 i 个数是 Ai , 你要找出 K 个不相邻的数, 使得他们的和最大。

请求出这个最大和。

【输入格式】

第一行两个整数 N 和 K。

接下来一行 N 个整数, 第 i 个整数表示 Ai 。

【输出格式】

一行一个整数表示最大和, 请注意答案可能会超过 int 范围

【样例输入】

3 2

4 5 3

【样例输出】

7

【数据范围】

对于 20% 的数据, N, K ≤ 20 。

对于 40% 的数据, N, K ≤ 1000 。

对于 60% 的数据, N, K ≤ 10000 。

对于 100% 的数据, N, K ≤ 100000 , 1 ≤ Ai ≤ 1000000000。

  这道题第一反应是DP的举起你们的双手,你们尽管打,A了算我输。

  这道题不得不说所有人都以为是DP,但DP再怎么优化也逃不开对N,K的枚举,因此只要是DP基本到n^2就已经是极限了,我就打了n^2,果然,60%,虽然不知道有人也是这个复杂度却过不了的原因,但n^2打的漂亮确乎是60分。因此DP这条路我们不得不放弃。

  那么问题来了,求最优解我们还能打啥呢?DP扑街了,那么只能是贪心了吧,那么怎么贪呢,见一个拿一个很明显是错解,那就让我们回到这个题目对贪心唯一的限制—相邻。

  相邻就意味着我一旦选了这个点,它周围的两个点都不能选了,那么怎么去利用这个性质呢?我们大可去把它和它周围两点连起来看,为什么不能在贪这个数的时候把它和其他数建立联系呢,那么问题来了,什么东西可以在极短的时间内找到它的前后两点(当然不算遍历),链表。

  我们大可用链表将该点与它左右两边还没被选上(我们暂且这么说,但从实际意义上来看它是不对的)的点连接起来,只要这个点被选上,就改变链表结构。

  大家可能看的一头雾水,好吧,我承认,我语文不好。现在开始讲贪心。

  首先我们先用优先队列将每个节点按照权值从大到小排列起来,同时保存每个节点所代表的区间的左右边界。每次从堆顶取元素时确认是否合法,即他的左右端点之一是否被覆盖,如果是就继续pop。

 “代表”“覆盖”是什么意思呢? 每次我们从堆顶选出来一个最大的位置,然后将其与左右两个相邻区间通过链表合并,并将这三个位置打上标记,以便删除,我们个左右两个区间打上标记并不是因为不再去考虑他们了,而是为了方便接下来的操作,我们要对当前点所维护的信息进行修改,首先是权值,权值改为作区间权值+右区间权值-原权值,这样当我们将这个节点再次拿出来的时候就可以去代表我们选择了它两端的权值了,然后将左右端点更新,链表更新即可,由于本题证明虽然容易但难以用语言表述,只能请读者自己证明了。

  

技术分享
 1 #include<iostream>
 2 #include<cstdlib>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<queue>
 6 #include<algorithm>
 7 #include<queue>
 8 #include<map>
 9 #include<cmath>
10 using namespace std;
11 int m,n;
12 long long a[100005];
13 map<int,bool> ma[100005];
14 struct no{
15     long long data;
16     int bh;
17     bool friend operator < (no a,no b)
18     {
19         if(a.data=http://www.mamicode.com/=b.data) return a.bh>b.bh;
20         return a.data<b.data;
21     }
22 };
23 priority_queue<no> q1;
24 int pre[100005],fro[100005];
25 int le[100005],ri[100005];
26 int main(){
27     scanf("%d%d",&n,&m);
28     for(int i=1;i<=n;i++)
29     {
30         scanf("%lld",&a[i]);
31         no x;
32         x.data=http://www.mamicode.com/a[i];
33         x.bh=i;
34         le[i]=i;
35         ri[i]=i;
36         pre[i]=i-1;
37         fro[i]=i+1;
38         q1.push(x);
39     }
40     long long ans=0;
41     fro[n]=0;
42     a[0]=-1e15;
43     while(m--)
44     {
45         while(!q1.empty()&&ma[le[q1.top().bh]][ri[q1.top().bh]])
46             q1.pop();
47         no x=q1.top();
48         q1.pop();
49         ans+=x.data;
50         a[x.bh]=-x.data;
51         x.data=http://www.mamicode.com/-x.data;
52         x.data+=a[pre[x.bh]],a[x.bh]+=a[pre[x.bh]];
53         x.data+=a[fro[x.bh]],a[x.bh]+=a[fro[x.bh]];
54         ma[le[pre[x.bh]]][ri[pre[x.bh]]]=ma[le[fro[x.bh]]][ri[fro[x.bh]]]=1;
55          
56         le[x.bh]=le[pre[x.bh]];
57         ri[x.bh]=ri[fro[x.bh]];
58         if(pre[pre[x.bh]])
59             fro[pre[pre[x.bh]]]=x.bh;
60         if(fro[fro[x.bh]])
61             pre[fro[fro[x.bh]]]=x.bh;
62         pre[x.bh]=pre[pre[x.bh]];
63         fro[x.bh]=fro[fro[x.bh]];
64         q1.push(x);
65     }
66     printf("%lld\n",ans);
67     return 0;
68 }
69  
View Code

  其实本题set可以更快,有兴趣的话可以试一试。

名校联赛DAY.2A层第二题SO(就)题解