[luogu1484]种树——神奇的贪心+堆优化

5 篇文章 0 订阅

题目大意:

给定一条长度为n的链,每一个点有一个权值,求不相邻地选小于等于k个点的总和的最大值

思路:

其实这题之前好像在哪里见过,但是由于当时没有好好理解,便还是想了一会,当时是一个环的情况,链的情况也差不多。DP其实很容易想,但是时间和空间上都过不去,所以可以考虑贪心,先去选那个权值最大的点,但是这不一定是最优的,我们发现,如果选最大的点不是最优的情况下,那么只有可能是选这个点限制了旁边两个点的选择,所以旁边两个点必须要全部都选,这样我们可以每次贪心的选取一个权值最大的点,并把以这个点为中心的三个点全部都缩成一个新的点,新的点的权值为a[i+1]+a[i-1]-a[i](假设原来的点的编号为i)这样当我们后面选的点的权值过小的时候,我们就可选择这个新缩成的点,就相当于不选i号点而选择了i+1号点和i-1号点,这样每次地贪心的选取,就相当于每一都会对前面的所有状态做一个颠倒,从而找到最后的最优的状态

做法:

就用优先队列就好了,删除点的时候不要从优先队列删掉,直接标记就好了,注意维护双向链表和堆中间的值。

/*===================
 * Author : ylsoi
 * Algorithm : tanxin
 * Time : 2018.4.1
 * ================*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
void File(){
#ifndef ONLINE_JUDGE
    freopen("luogu1484.in","r",stdin);
    freopen("luogu1484.out","w",stdout);
#endif
}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define ll long long
#define inf (0x3f3f3f3f)
const int maxn=5e5+10;
int n,k,l[maxn],r[maxn],head;
ll a[maxn],ans;
bool vis[maxn];
struct node{
    int id;
    bool operator < (const node &tt) const {
        return a[id]<a[tt.id];
    }
};
priority_queue<node>q;
int main(){
    File();
    scanf("%d%d",&n,&k);
    REP(i,1,n)scanf("%lld",&a[i]);
    REP(i,1,n)l[i]=i-1,r[i]=i+1;
    r[n]=0;
    REP(i,1,n)q.push((node){i});
    while(k--){
        while(vis[q.top().id])q.pop();
        node t=q.top();
        q.pop();
        int u=t.id;
        if(a[u]<=0)break;
        ans+=a[u];
        if(!l[u]){
            vis[u]=vis[r[u]]=1;
            l[r[r[u]]]=0;
        }
        else if(!r[u]){
            vis[u]=vis[l[u]]=1;
            r[l[l[u]]]=0;
        }
        else{
            vis[l[u]]=vis[r[u]]=1;
            a[u]=a[l[u]]+a[r[u]]-a[u];
            q.push((node){u});
            r[l[l[u]]]=u;l[u]=l[l[u]];
            l[r[r[u]]]=u;r[u]=r[r[u]];
        }
    }
    cout<<ans<<endl;
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值