Evanyou Blog 彩带

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以内。

 

Solution:

  本题的二叉堆的做法实在是太巧妙了!!!

  首先,我打了一个普通的$DP$,期望得分$50$,很容易想到状态$f[i][j],\;i\in[1,n],\;j\in[1,k]$表示前$i$个位置中了$j$棵树的最大获利,则由题目限制条件不难想到状态转移方程:$f[i][j]=max(f[i-1][j],f[i-2][j-1]+a[i])$,注意下边界和初始状态就有$50$分了。时间空间复杂度都是$O(n^2)$,显然不行。

  此时想了各种奇技淫巧,依然没用。。。还是默默的看了下题解,惊叹于本题的二叉堆做法:

  我们先进行小规模枚举:
  $k=1$时,显然取$n$个数中取最大的即可(暂不考虑全负的情况)。设最大的数是$a[i]$。

  $k=2$时,则有两种可能:1、另取一个与$a[i]$不相邻的$a[j]$。2、取$a[i-1]$和$a[i+1]$。

  我们可以发现:如果$k=1$时最优解为$a[i]$,那么我们便可以把$a[i-1]$和$a[i+1]$进行合并,因为它们要么同时被选,要么同时落选(证明不难,请自行解决)。而且,我们还注意到:当选了$a[i-1]$和$a[i+1]$时,获利便增加了$a[i-1]+a[i+1]-a[i]$。所以当$a[i]$被选时,我们就可以删去$a[i-1]$和$a[i+1]$,并把$a[i]$改成$a[i-1]+a[i+1]-a[i]$(即使为负也没问题,因为下次不会选它,而若为正则等同于选了$a[i-1]$和$a[i+1]$),重新找最大的。

  每次找的都是最大的数,我们便可以使用堆进行操作,直到堆中最大值小于等于$0$或取出$k$个数后停止。复杂度$O(nlogn)$。

 

1、先安利一下自己$DP$的代码:

#include<bits/stdc++.h>
#define il inline
#define ll long long
using namespace std;
const int N=10005;
ll n,k,f[N][5000],ans=-100000000;
int a[N];
il ll gi(){
    ll a=0;char x=getchar();bool f=0;
    while((x<'0'||x>'9')&&x!='-')x=getchar();
    if(x=='-')x=getchar(),f=1;
    while(x>='0'&&x<='9')a=a*10+x-48,x=getchar();
    return f?-a:a;
}
int main(){
    n=gi(),k=gi();
    for(int i=1;i<=n;i++)a[i]=gi();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=k;j++)f[i][j]=-100000000;
    f[1][1]=a[1];
    for(int i=2;i<=n;i++)
        for(int j=1;j<=k&&j<=i;j++){
        if(i-2>0)f[i][j]=max(f[i-1][j],f[i-2][j-1]+a[i]);
        else f[i][j]=f[i-1][j];
        ans=max(f[i][j],ans);
    }
    cout<<ans;
    return 0;
}

 

 

2、再发一波正解代码:

#include<bits/stdc++.h>
#define il inline
#define ll long long
using namespace std;
const int N=500005;
int n,k;
ll ans,tot,a[N],l[N],r[N],pos;
struct node{
    int v,id;
    bool operator < (const node a)const {return v<a.v;}
}tmp;
priority_queue<node>q;
bool vis[N];
il ll gi(){
    ll a=0;char x=getchar();bool f=0;
    while((x<'0'||x>'9')&&x!='-')x=getchar();
    if(x=='-')x=getchar(),f=1;
    while(x>='0'&&x<='9')a=a*10+x-48,x=getchar();
    return f?-a:a;
}
int main(){
    n=gi(),k=gi();
    for(int i=1;i<=n;i++){
        tmp.v=gi(),tmp.id=i;q.push(tmp);
        l[i]=i-1;r[i]=i+1;a[i]=tmp.v;
    }
    r[0]=1,l[n+1]=n;
    while(k--){
        while(vis[q.top().id])q.pop();
        tmp=q.top();q.pop();
        if(tmp.v<0)break;
        ans+=tmp.v;pos=tmp.id;
        a[pos]=a[l[pos]]+a[r[pos]]-a[pos];
        tmp.v=a[pos];
        vis[l[pos]]=vis[r[pos]]=1;
        l[pos]=l[l[pos]],r[l[pos]]=pos;
        r[pos]=r[r[pos]],l[r[pos]]=pos;
        q.push(tmp);
    }
    cout<<ans;
    return 0;
}

 

 

posted @ 2018-04-26 21:06  five20  阅读(435)  评论(1编辑  收藏  举报
Live2D