P1484 种树

传送门

瞄一眼

显然DP

再瞄一眼

方程就出来了:

设f[ i ][ j ]表示考虑到第 i 颗树,已经种了 j 颗的最大价值.

则 f[ i ][ j ]=max(f[ i-1][ j ],f[ i-2 ][ j-1]+value[ i ]);

最后看了眼数据..

凉了....

考虑优化

想死也想不出来...........

最后看了题解才懂...

洛沽的题解(不是我的)

 


 

贪心

如果选择一个点种树

那么它两边的点都不能种

当只种 1 颗时

如果选择第 i 个点最优,那就选 i 

种 2 颗时,如果要放弃 i 而选 i 两边的点

那么就一定要两点一起选

因为如果放弃 i 后只选一边的点,那还不如选 i(因为i 的价值肯定大于一边的点,不然前面就不会选 i 了)

而且选了旁边的点更旁边的点就不能选

所以如果放弃 i 就一定要一起选旁边的两点

显然 如果放弃 i 选旁边两点 那么增加的价值为:value[ i-1]+value[ i+1]-value[ i ]

那么我们可以抽象地添加一个点 a ,a=value[ i-1]+value[ i+1]-value[ i ]

如果选了 a 就表示放弃了 i 从而选 i 两边的点

在什么时候添加点 a 呢

显然是在选了点 i 以后..

那么我们就可以贪心了:

选最大的点,更新答案后扔掉,添加 a 到数列里

再选最大的点,一直这样,直到选了 k 个点

(就算选的是抽象出来的点也可以,原因很简单,稍微画个图就懂了)

怎么维护也很简单

开个结构体,用一个堆维护就好了

剩下就是一些细节了,看程序就好了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
struct node
{
    long long v,id;
    bool operator < (const node &b) const{
        return v<b.v;
    }
};
priority_queue <node> q;
long long n,k,a[500007],l[500007],r[500007],ans;
bool pd[500007];
int main()
{
    node p;
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        l[i]=i-1; r[i]=i+1;
        p.v=a[i]; p.id=i;
        q.push(p);
    }
    int x;
    while(k--)
    {
        while(pd[q.top().id]) q.pop();
        p=q.top(); q.pop();
        if(p.v<=0) break; //细节,如果出现负数就直接退出,选了反而更小
        ans+=p.v;
        x=p.id;
        a[x]=a[l[x]]+a[r[x]]-a[x];
        pd[l[x]]=pd[r[x]]=1;//细节
        l[x]=l[l[x]]; r[x]=r[r[x]];//细节
        r[l[x]]=x; l[r[x]]=x;//细节
        p.v=a[x];
        q.push(p);
    }//这里有一堆细节
    cout<<ans;
    return 0;
}

 

posted @ 2018-08-25 11:31  LLTYYC  阅读(209)  评论(0编辑  收藏  举报