洛谷P1484 种树 贪心+堆

你。 提交于 2020-02-26 23:08:24

题目链接:https://www.luogu.com.cn/problem/P1484

一开始看到这题,想到用DP来做,但是n<=50,000(实在是太大了)。所以就换个思路,贪心
假设 k=1 即只种一棵树,那肯定种盈利最大的那个,设为 i ,盈利为a[i]。
若 k=2 要种两颗树了,此时我们只有两种选择,种 i 以及和它不相邻的那个坑,或者种 i 左右的两坑,即 i-1 与 i+1。因为,如果只种左右俩坑其中一个,肯定盈利比a[i]小,因为a[i]是一开始选得最大的那个。
有了这个结论,我们就可以合并 i-1 与 i+1。建一个大根堆,每次取出最大的那个,然后合并它的左右坑,使a[i]=a[i+1]+a[i-1]-a[i],重新入堆,如果已种了k个树或者再种盈利为负,就结束。
代码如下

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e5+5;
struct node
{
    int id;
    ll value;
    friend bool operator < (node a,node b)
    {
        return a.value < b.value;
    }
};
priority_queue<node>q;
bool vis[maxn];//判断是否被合并
ll a[maxn];//每个坑种树的盈利
int l[maxn],r[maxn];//每个坑的左右
int n,k;
ll ans;
int main()
{
    scanf("%d %d",&n,&k);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        l[i]=i-1;
        r[i]=i+1;
        q.push({i,a[i]});
    }
    while(k--)
    {
        while(vis[q.top().id])
        q.pop();
        if(q.top().value<0)//没有坑盈利为正,直接退出·
        break;
        node temp=q.top();
        q.pop();
        ans+=temp.value;
        int x=temp.id;
        vis[l[x]]=vis[r[x]]=1;
        a[x]=a[l[x]]+a[r[x]]-a[x];//合并左右两坑
        temp.value=a[x];
        q.push(temp);
        l[x]=l[l[x]],r[x]=r[r[x]];
        l[r[x]]=x,r[l[x]]=x;
    }
    printf("%lld\n",ans);
    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!