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$的代码:

 1 // luogu-judger-enable-o2
 2 #include<bits/stdc++.h>
 3 #define il inline
 4 #define ll long long
 5 using namespace std;
 6 const int N=10005;
 7 ll n,k,f[N][5000],ans=-100000000;
 8 int a[N];
 9 il ll gi(){
10     ll a=0;char x=getchar();bool f=0;
11     while((x<'0'||x>'9')&&x!='-')x=getchar();
12     if(x=='-')x=getchar(),f=1;
13     while(x>='0'&&x<='9')a=a*10+x-48,x=getchar();
14     return f?-a:a;
15 }
16 int main(){
17     n=gi(),k=gi();
18     for(int i=1;i<=n;i++)a[i]=gi();
19     for(int i=1;i<=n;i++)
20         for(int j=1;j<=k;j++)f[i][j]=-100000000;
21     f[1][1]=a[1];
22     for(int i=2;i<=n;i++)
23         for(int j=1;j<=k&&j<=i;j++){
24         if(i-2>0)f[i][j]=max(f[i-1][j],f[i-2][j-1]+a[i]);
25         else f[i][j]=f[i-1][j];
26         ans=max(f[i][j],ans);
27     }
28     cout<<ans;
29     return 0;
30 }

2、再发一波正解代码:

 1 // luogu-judger-enable-o2
 2 #include<bits/stdc++.h>
 3 #define il inline
 4 #define ll long long
 5 using namespace std;
 6 const int N=500005;
 7 int n,k;
 8 ll ans,tot,a[N],l[N],r[N],pos;
 9 struct node{
10     int v,id;
11     bool operator < (const node a)const {return v<a.v;}
12 }tmp;
13 priority_queue<node>q;
14 bool vis[N];
15 il ll gi(){
16     ll a=0;char x=getchar();bool f=0;
17     while((x<'0'||x>'9')&&x!='-')x=getchar();
18     if(x=='-')x=getchar(),f=1;
19     while(x>='0'&&x<='9')a=a*10+x-48,x=getchar();
20     return f?-a:a;
21 }
22 int main(){
23     n=gi(),k=gi();
24     for(int i=1;i<=n;i++){
25         tmp.v=gi(),tmp.id=i;q.push(tmp);
26         l[i]=i-1;r[i]=i+1;a[i]=tmp.v;
27     }
28     r[0]=1,l[n+1]=n;
29     while(k--){
30         while(vis[q.top().id])q.pop();
31         tmp=q.top();q.pop();
32         if(tmp.v<0)break;
33         ans+=tmp.v;pos=tmp.id;
34         a[pos]=a[l[pos]]+a[r[pos]]-a[pos];
35         tmp.v=a[pos];
36         vis[l[pos]]=vis[r[pos]]=1;
37         l[pos]=l[l[pos]],r[l[pos]]=pos;
38         r[pos]=r[r[pos]],l[r[pos]]=pos;
39         q.push(tmp);
40     }
41     cout<<ans;
42     return 0;
43 }

猜你喜欢

转载自www.cnblogs.com/five20/p/8955018.html