BZOJ2151 种树 [贪心+链表]

Posted crazydave

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ2151 种树 [贪心+链表]相关的知识,希望对你有一定的参考价值。

Description

A城市有一个巨大的圆形广场,为了绿化环境和净化空气,市政府决定沿圆形广场外圈种一圈树。

园林部门得到指令后,初步规划出n个种树的位置,顺时针编号1到n。并且每个位置都有一个美观度Ai,如果在这里种树就可以得到这Ai的美观度。但由于A城市土壤肥力欠佳,两棵树决不能种在相邻的位置(i号位置和i+1号位置叫相邻位置。值得注意的是1号和n号也算相邻位置!)。

最终市政府给园林部门提供了m棵树苗并要求全部种上,请你帮忙设计种树方案使得美观度总和最大。如果无法将m棵树苗全部种上,给出无解信息。

Input

输入的第一行包含两个正整数n、m。第二行n个整数Ai。

Output

输出一个整数,表示最佳植树方案可以得到的美观度。如果无解输出“Error!”,不包含引号。

Sample Input

7 3

1 2 3 4 5 6 7

Sample Output

15

Hint

对于全部数据:m<=n; -1000<=Ai<=1000; n<=200000;

思路

这道题目看上去很容易想到是DP。实际上却是一种神贪心。。。都是套路

每次贪心选取最大值,答案加上这个最大值,最大值左右两边当然不能再选,于是就标记为访问过。最大值不难想到我们可以用堆进行维护。

但是这个贪心显然是不完善的。当最大值左右两边之和大于它本身时,就会产生错误。于是就有一种神奇的操作:将左右两边之和减去最大值,作为新的节点,覆盖掉原来的最大值,并删去左右两个节点。

原因是要么选最大值,要么选它的左右之和,选最大值时答案加上这个最大值,不再去选新的节点,选左右之和时答案还是加上最大值,只不过后面还要加上新节点的值。也就是当我们再次选取的新节点时,也就相当于选取当时最大值的左右之和,而没有选最大值。

这个神奇的操作有删除左右节点的过程,因此我们要用双向链表进行维护。总的来说这个思路比较难想到,但是代码还是比较好打的。

代码

需要注意的细节:
1.链表时环状的,第一个节点与最后一个相邻
2.进行删除节点时要注意前驱和后继都要修改
3.操作的顺序要注意,先更新值再进行删除

#include <bits/stdc++.h>
#define MAXN 200005

int n, m, ans, num[MAXN], pre[MAXN], next[MAXN];
bool vis[MAXN];

struct node
{
    int v, p;
    bool operator < (const node &x) const
    {
        return v<x.v;
    }
}; 
std::priority_queue <node> q;  //STL优先队列,建立大根堆

int main()
{
    scanf("%d%d",&n,&m);
    if(m*2>n) {printf("Error!\n"); return 0;}  //特判无解情况
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&num[i]);
        q.push((node){num[i], i});
        pre[i]=i-1; next[i]=i+1;  //数组模拟链表
    }
    pre[1]=n; next[n]=1; 
    for(int i=1; i<=m; i++)
    {
        while(vis[q.top().p]) q.pop();  //访问过的直接出队
        node top=q.top(); q.pop();
        ans+=top.v;
        vis[next[top.p]]=vis[pre[top.p]]=1;  //将左右节点标记为访问过的
        num[top.p]=num[pre[top.p]]+num[next[top.p]]-num[top.p]; 
        //删除左右节点
        pre[top.p]=pre[pre[top.p]];
        next[top.p]=next[next[top.p]];
        next[pre[top.p]]=top.p;
        pre[next[top.p]]=top.p;
        q.push((node){num[top.p], top.p});
    }
    printf("%d\n",ans);
} 

以上是关于BZOJ2151 种树 [贪心+链表]的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 2151 种树(可反悔贪心,链表)BZOJ千题计划就图一乐

BZOJ 2151 种树(可反悔贪心,链表)BZOJ千题计划就图一乐

BZOJ 2151 2151: 种树 (贪心+堆)

bzoj2151种树(堆/优先队列+双向链表)

bzoj 2151: 种树贪心+堆

bzoj2151: 种树(双向链表+堆)