Luogu P1484 种树

这道题目还是比较简单的

首先题目的意思就让我们很轻易地想到DP

我们设f[i][j]表示前i个坑中种j棵树的最大利益,则有:

f[i][j]=max(f[i-1][j],f[i-2][j-1]+a[i])

然而对于本题的数据范围之能得50pts

要A掉的话还是要动一些脑子的

我们先从小的情况开始讨论:

  • 当k=1时,我们只需要找一个最大的收益即可(当然全负就不要找了)

  • 当k=2时,我们先挑选一个最大的,若接下来最大的不是这个数两侧的数,那就区接下来最大的数即可

  • 当k=2时,当然可能会有情况是选这两个数相邻的两个数(当然也只有这种情况)。表述得清楚一些就是若第一次选了a[i],除非最优解是a[i-1]+a[i+1],否则都是上面的选法最优

以此我们发现对于已经被选择的a[i],a[i-1],a[i+1]要么同时被选,要么同时落选

因此我们开一个大根堆,每次取出a[i]之后把它的值累加至ans后,将a[i]的值改成a[pre[i]]+a[nxt[i]]-a[i] (这里的pre[i]表示i的前驱,nxt[i]表示i的后继,刚开始就分别是i-1,i+1)

为什么呢,因为我们想一下,这样就给了程序一个返回的机会,此时当你选择了新的a[i]时,其实就是选择了原来的a[pre[i]]和a[nxt[i]]

然后开一个STL堆即可水过

CODE

#include<cstdio>
#include<queue>
using namespace std;
const int N=500005;
struct node
{
    int v,num;
    bool operator <(const node &s) const
    {
        return s.v>v;
    }
};
priority_queue <node> big;
int a[N],pre[N],nxt[N],n,k,tot;
bool vis[N];
long long ans;
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
    x=0; char ch=tc(); int flag=1;
    while (ch<'0'||ch>'9') { if (ch=='-') flag=-1; ch=tc(); }
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
    x*=flag;
}
int main()
{
    //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    register int i;
    read(n); read(k); 
    for (i=1;i<=n;++i)
    read(a[i]),pre[i]=i-1,nxt[i]=i+1,big.push((node){a[i],i});
    nxt[0]=1; pre[n+1]=n;
    while (tot<k)
    {
        while (vis[big.top().num]) big.pop();
        node p=big.top(); big.pop();
        if (p.v<0) break; ++tot; ans+=p.v;
        p.v=a[p.num]=a[pre[p.num]]+a[nxt[p.num]]-a[p.num];
        vis[pre[p.num]]=vis[nxt[p.num]]=1;
        pre[p.num]=pre[pre[p.num]]; nxt[pre[p.num]]=p.num;
        nxt[p.num]=nxt[nxt[p.num]]; pre[nxt[p.num]]=p.num;
        big.push(p);
    }
    printf("%lld",ans);
    return 0;
}
posted @ 2018-05-11 18:33  空気力学の詩  阅读(132)  评论(0编辑  收藏  举报