BZOJ 2151 种树 贪心 双向链表优化

11 篇文章 0 订阅
5 篇文章 0 订阅

给你一个 n 个点的环,每个点有点权,在环上取出互不相邻的m个点,求最大值
我们可以贪心,将所有的点扔入一个堆里
每次取出堆顶的元素,然后在双向链表中删去这个点
然而这样可能是错的,比如 1354
正确答案应该是7
所以我们要给自己退路
在选择一个元素的时候,将该元素的值改为两边元素的和减去该元素,然后删去两边的元素
然后选择 m

我们来想他的正确性
首先:就算这两个不是最优的,我们下一次不会再选回来,一定可以保证答案的最优性
其次:我们不会选少,每次如果选出一个没选过的,肯定没问题,如果是一个选过的周边,也不会有什么问题,因为我们减去了原来的权值,保证了答案的完备性

其实画画就明白了,我就是没事瞎想的……总之对就是了
时间复杂度mlog(n)
TIPS 我的链表是数组的
所以喜欢指针的大神可以关掉页面了……

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <queue>
#define N 200000+5
using namespace std;

inline int read()
{
    int x=0,f=1;char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-')f=-1;ch = getchar();}
    while(ch >='0' && ch <='9'){x=(x<<1)+(x<<3)+ch-'0';ch = getchar();}
    return x*f;
}

struct data
{
    int pos;
    int val;
    bool operator < (const data &z)const
    {
        return val < z.val;
    }
    data () {}
    data (int _pos,int _val)
    :pos(_pos),val(_val){}
};

priority_queue<data>Q;

int L[N],R[N],a[N];
bool del[N];
int ans = 0;

void work()
{
    while(del[Q.top().pos])
        Q.pop();
    data tmp = Q.top();Q.pop();
    int tt = tmp.pos;
    ans += a[tt];
    a[tt] = a[L[tt]] + a[R[tt]] - a[tt];
    Q.push(data(tt,a[tt]));
    R[L[L[tt]]] = tt;
    L[R[R[tt]]] = tt;
    del[R[tt]] = del[L[tt]] = 1;
    L[tt] = L[L[tt]];
    R[tt] = R[R[tt]];
}

int main()
{
    int n = read(), m = read();
    if(m > (n>>1)){puts("Error!");return 0;}
    for(int i=1;i<=n;++i)
    {
        a[i] = read();
        Q.push(data(i,a[i]));
        L[i] = i-1;
        R[i] = i+1;
    }
    L[1] = n;
    R[n] = 1;
    while(m--)
        work();
    cout<<ans;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值