洛谷 P1484 種樹

題目鏈接

題目描述

cyrcyr今天在種樹,他在一條直線上挖了n個坑。這n個坑都可以種樹,但爲了保證每一棵樹都有充足的養料,cyrcyr不會在相鄰的兩個坑中種樹。而且由於cyrcyr的樹種不夠,他至多會種k棵樹。假設cyrcyr有某種神能力,能預知自己在某個坑種樹的獲利會是多少(可能爲負),請你幫助他計算出他的最大獲利。

輸入輸出格式

輸入格式:
第一行,兩個正整數n,k。

第二行,n個正整數,第i個數表示在直線上從左往右數第i個坑種樹的獲利。

輸出格式:
輸出1個數,表示cyrcyr種樹的最大獲利。

輸入樣例#1:
6 3
100 1 -1 100 1 -1

輸出樣例#1:
200

說明

對於20%的數據,n<=20。

對於50%的數據,n<=6000。

對於100%的數據,n<=500000,k<=n/2,在一個地方種樹獲利的絕對值在1000000以內。


首先應該想到的是O(nk)的dp, 可以獲得50分。

正解:貪心+堆(大根堆)

我們可以把選擇分爲兩種, 選擇 a[i] 和選擇 a[i-1] 、a[i+1]
這樣,將坑每3個分成一組,對於每個a[i], 要麼就選擇a[i],要麼就a[i-1],a[i+1]一起選。
然後堆優化貪心。具體貪心看代碼。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 500010;
struct node
{
    ll v;//價值
    int id;//編號
    bool operator <(node b) const
    {
        return v < b.v;
    }
};
ll a[N], ans;
int l[N], r[N];//鏈表
bool bj[N];//是否可以選擇
priority_queue<node> q;
int main()
{
    int n, k;
    scanf("%d%d", &n, &k);
    for(int i = 1; i <= n; i++)
    {
        scanf("%lld", &a[i]);
        q.push((node){a[i], i});//默認第一種選擇
        l[i] = i-1, r[i] = i+1;
    }
    int len = n;
    for(int i = 1; i <= k; i++)
    {
        while(!q.empty() && bj[q.top().id])
            q.pop();
        if(q.empty() || q.top().v < 0)
            break;//如果堆頂的價值小於0或沒有選擇的,就沒必要繼續選擇,直接退出
        node t = q.top();q.pop();
        ans += t.v;
        bj[t.id] = bj[l[t.id]] = bj[r[t.id]] = 1;//標記不能選
        a[++len] = a[l[t.id]]+a[r[t.id]]-a[t.id];
        /*新添加一個點, -a[t.id]是做反悔打算,
         即不選擇a[t.id]而選擇a[l[t.id]]和a[r[t.id]]*/
        int l1 = l[t.id], r1 = r[t.id];
        l[len] = l[l1], r[len] = r[r1];
        r[l[len]] = len, l[r[len]] = len;//把三個點合成一個點
        q.push((node){a[len], len});
    }
    printf("%lld\n", ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章