洛谷 P1484 种树 思维+堆

洛谷 P1484 种树 思维+堆

思路:

很容易想到用一个大根堆来存储每个位置的获利,但我们不能每次都取出根元素后,将其两边的元素标记位不可选择,因为这个根元素两边的元素之和很可能大于这个根元素,这时我们选择的就不是根元素,而是两边的元素,所以我们要有一个反悔选择的机会,我们在选择根元素的时候,在总获利中加上其获利,并把这个根元素的获利变为两边元素的获利之和减去根元素的获利,并将这个根元素重新放入大根堆,这样我们就有了反悔的机会,同时需要删除两边的元素,因为它们已经用不到了。

代码如下:

#include<iostream>
#include<queue>
#include<stdio.h>
#define MAX 500005
using namespace std;

int n,k,flag[MAX]={0},num[MAX];//num记录在某个地方的获利
long long tot=0;//总获利
int l[MAX],r[MAX];

struct node{
    int key;
    int loc;
    node(int k,int l){
        key=k;
        loc=l;
    }
    bool operator < (const node &a) const{
        return key<a.key;//最大值优先
    }
};

priority_queue<node> q;

int main(){
    scanf("%d%d",&n,&k);
    int tmp;
    for(int i=1;i<=n;i++){
        scanf("%d",&tmp);
        q.push(node(tmp,i));
        l[i]=i-1;
        r[i]=i+1;
        num[i]=tmp;
    }

    l[0]=1,r[n+1]=n;

    for(int i=0;i<k;i++){
        while(flag[q.top().loc]==1){//删除已经被删除的根顶元素
            q.pop();
        }
        node t=q.top();
        q.pop();
        if(t.key<=0){//如果堆顶元素小于等于0,则跳出循环
            break;
        }
        tot+=t.key;//总获利增加t.key
        int id=t.loc;
        num[id]=num[l[id]]+num[r[id]]-num[id];
        t.key=num[id];
        flag[l[id]]=flag[r[id]]=1;//标记左右坑不可选择
        l[id]=l[l[id]];//id的左坑变为它左坑的左坑
        r[id]=r[r[id]];//id的右坑变为它右坑的右坑
        l[r[id]]=id;//id右坑的左坑变为id
        r[l[id]]=id;//id左坑的右坑变为id
        q.push(t);

    }
    printf("%lld",tot);
    return 0;
}

发布了253 篇原创文章 · 获赞 15 · 访问量 7998

猜你喜欢

转载自blog.csdn.net/weixin_44123362/article/details/103878044