P1484 种树

P1484 种树

题目描述
cyrcyr今天在种树,他在一条直线上挖了n个坑。这n个坑都可以种树,但为了保证每一棵树都有充足的养料,cyrcyr不会在相邻的两个坑中种树。而且由于cyrcyr的树种不够,他至多会种k棵树。假设cyrcyr有某种神能力,能预知自己在某个坑种树的获利会是多少(可能为负),请你帮助他计算出他的最大获利。

输入输出格式
输入格式:
第一行,两个正整数n,k。

第二行,n个正整数,第i个数表示在直线上从左往右数第i个坑种树的获利。

输出格式:
输出1个数,表示cyrcyr种树的最大获利。


第一种思路DP:\(dp[i][j]\) 代表前\(i\)个格子种\(j\)棵树的最大获利,有dp方程 \(dp[i][j] = max(dp[i - 1][j],dp[i - 2][j - 1])\)

然而数据范围不允许这\(O(n ^ {2})\)的算法

用数学归纳法可以证明:对于一个位置,我们要么取当前,要么不取当前取两旁,最后答案最大

我们创建一个大根堆,每次取最大值加入答案中,标记一下左右两边不可取,再把 \(a[i -1] + a[i + 1] - a[i]\)丢入堆中

值得注意的是,为了除去第一个值和最后一个值对答案的影响(虽然在本题没有体现),我们给第0个和最后一个赋一个极小值

为什么呢?这里我们提供了一个反悔选项,若是下次选最大值选到了\(a[i -1] + a[i + 1] - a[i]\),观察这两次的结果:前一次:\(ans + a[i]\),后一次:\(ans + a[i] + a[i - 1] + a[i +1] - a[i] = a[i - 1] + a[i + 1]\),这不就是反悔,选择两旁而不选中间了吗

复杂度 \(O(k\log\ N)\)

Code

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#include<climits>
typedef long long LL;
using namespace std;
LL RD(){
    LL out = 0,flag = 1;char c = getchar();
    while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
    while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
    return flag * out;
    }
const int maxn = 5000019;
struct Node{
    LL val,index;
    Node (int val, int index):val(val), index(index){}
    Node(){}
    bool operator < (const Node &a)const{return val < a.val;}
    }I[maxn];
priority_queue<Node>Q;
LL num,k;
LL a[maxn];
bool used[maxn];
LL ans;
int l[maxn],r[maxn];
int main(){
    num = RD();k = RD();
    for(int i = 1;i <= num;i++){
        l[i] = i - 1,r[i] = i + 1;
        a[i] = RD();
        Q.push(Node(a[i],i));
        }
    while(k--){
        while(used[Q.top().index])Q.pop();
        Node now = Q.top();Q.pop();
        if(now.val < 0)break;
        ans += now.val;
        int p = now.index;
        a[p] = a[l[p]] + a[r[p]] - a[p];
        Q.push(Node(a[p],p));
        used[l[p]] = used[r[p]] = 1;
        l[p] = l[l[p]],r[p] = r[r[p]];
        r[l[p]] = p,l[r[p]] = p;
        }
    printf("%lld\n",ans);
    return 0;
    }
posted @ 2018-07-10 14:12  Tony_Double_Sky  阅读(280)  评论(0编辑  收藏  举报