1. 程式人生 > >P1484 種樹 - 堆 - 貪心

P1484 種樹 - 堆 - 貪心

top val per ont include 都在 scan 就是 但是

這題想得腦闊疼。。。我只想到可以選一個點,或者不選這個點選其左右兩個點的和

先來特殊情況,k=1, 然後k=2
可以發現由1到2的過程中,可以是一個本來被選的點,替換為他左右兩邊的點,收益增加了a[pos+1] + a[pos-1] - a[pos]
這個題是一個個選,直到選了k個,有種遞推的感覺,先確定種了前面幾個,再確定這一個該怎麽種

然後我不會處理若有別的點也選上,並且影響到這個pos+1和pos-1的情況

事實上可以把這三個點縮在一起啊(當然不能提前縮,要在pos這個點被選時縮起來)
然後我若再選一個別的點,若這個別的點會影響到pos+1或pos-1,因為縮了點,pos+1和pos-1現在都在pos上,所以這個“別的點”就會讓pos的值不可用,由於我們把值也縮在pos上了,直接導致這個pos替換為pos+1和pos-1不可用了,這也符合題意,並且更好寫

但是還是有點難處理怎麽縮點,縮點之後應當刪除3個點,新建一個點,而原來其他位於這三個點左邊最靠右的點,若我們選了這個點,這說明這三個點就不可選了,也就是新點不可選,所以要把原來位於左邊最靠右的點的右邊連到新點上,這就需要開幾個數組模擬鏈表什麽的了

堆的題拆點(一個點拆成兩個,可以是等大的兩個,對左右兩邊起一個橋梁作用)和縮點(刪點)比較多

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int MAXN = 1000000 + 10;
typedef long long ll;
ll n,k,a[MAXN],ans,flg[MAXN],cnt,l[MAXN],r[MAXN]; 
bool fina;
struct node{
    int val, pos;
    bool operator < (const node & a) const {
        return val < a.val;
    }
};
priority_queue<node> q;
int main() {
    scanf("%lld%lld", &n, &k);
    for(int i=1; i<=n; i++) {
        scanf("%lld", &a[i]);
        l[i] = i-1;
        r[i] = i+1;
        q.push((node){a[i], i});
    }
    int tot = n;
    while(!q.empty()) {
        node now = q.top();
        q.pop();
        if(now.val <= 0) break;
        int pos = now.pos;
        if(flg[pos]) continue;
        if(++cnt > k) break;
        ans += now.val;
        a[++tot] = a[l[pos]] + a[r[pos]] - a[pos];
        flg[pos] = flg[r[pos]] = flg[l[pos]] = 1;
        l[tot] = l[l[pos]], r[tot] = r[r[pos]];
        r[l[tot]] = tot;
        l[r[tot]] = tot;
        q.push((node){a[tot], tot});
    } 
    printf("%lld", ans);
    return 0;
}

P1484 種樹 - 堆 - 貪心