题解——种树(堆)

题解——种树(堆)

考试题目叫 so , 和这道种树几乎差不多


题面

Description

cyrcyr今天在种树,他在一条直线上挖了n个坑。这n个坑都可以种树,但为了保证每一棵树都有充足的养料,cyrcyr不会在相邻的两个坑中种树。而且由于cyrcyr的树种不够,他至多会种k棵树。假设cyrcyr有某种神能力,能预知自己在某个坑种树的获利会是多少(可能为负),请你帮助他计算出他的最大获利。

Input

第一行,两个正整数n,k。

第二行,n个正整数,第i个数表示在直线上从左往右数第i个坑种树的获利。

Output

输出1个数,表示cyrcyr种树的最大获利。

in.1
6 3
100 1 -1 100 1 -1

out.1
200

数据范围与约定

对于20%的数据,n<=20。

对于50%的数据,n<=6000。

对于100%的数据,n<=500000,k<=n/2,在一个地方种树获利的绝对值在1000000以内。

部分分做法

20分做法:

想怎样暴力,就怎样暴力。20分暴力做法之多。

50分做法:

DP,转移并不是特别难。
我们定义 dp[ i ][ j ][ 1/0 ] 表示递推到第 i 位,已经选了 j 个数, 1/0 表示当前位是否选择
实际上和没有上司的舞会的dp转移很像,不过是序列上的

dp[ i ][ j ][ 1 ] = dp[ i-1 ][ j-1 ][ 0 ]
dp[ i ][ j ][ 0 ] = max( dp[ i-1 ][ j ][ 0 ] , dp[ i-1 ][ j ][ 1 ] )

答案取 max{ dp[ N ][ K ][ 1/0 ] } , 滚掉第一位以节省空间

100% 数据思路

主要思路

1.处理选择矛盾的情况,即可以反悔。

我们考虑,当选择了一个当前最大值 a[ i ] 后,发现其实最优值要选择a[ i-1 ] 和 a[ i+1 ], 为了可以反悔,在选择下一个时,我们把 a[ i ]修改为 a[ i-1 ] + a[ i+1 ] - a[i] 。所以当 a[i] 被选后反悔时,我们在之后的选择中,我们就可以删去先前 a[ i ]的贡献而选择旁边两个。如果不反悔则不选,这样可以满足整个结构的最优性。

2.维护序列,不断满足上述条件

最大值我们考虑使用大根堆(优先队列来维护),先把所有值和编号压入堆,并且记录左右值的编号,在选择了 a[ i ]后进行上述操作,同时更行左右值得编号即可。整个操作进行K次,由于存在负值,所以要不断取最大值。

细节:
边界是不能合并越界,处理下即可。

并查集版本(std)

#include<bits/stdc++.h>
using namespace std ;
#define ll long long
const int MAXN = 500005  ;
inline int read(){
    int s=0,w=1 ; char g=getchar() ; while(g>'9'||g<'0'){ if( g=='-')w=-1;g=getchar() ;} 
    while(g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ; return s*w ;
}
priority_queue< pair<ll,int> >q ;
int N , K , tot ; 
ll ans = 0 , last = 0 ;
struct ap{
    int  l , r ; 
    ll val ;
    bool f ; 
}t[ MAXN ];
int main(){
    t[ 0 ].val = -10000000000000005LL ; //边界
    N = read() , K = read() ; tot = N ;
    for( int i = 1 ; i <= N ; ++i ){
        t[ i ].val = (ll)read() , t[ i ].l = i-1 , t[ i ].r = i+1 ;
        q.push( make_pair(t[ i ].val , i) ) ;
    }
    t[ N ].r = 0 ;//边界
    while( K-- ){
        int u = q.top().second ;
        if( t[ u ].f ){ K++;q.pop();continue; }
        ll v = q.top().first ; q.pop() ;
        ans += v ; t[ t[ u ].l ].f = t[ t[ u ].r ].f = true ;//更新答案
        t[ u ].val = t[ t[ u ].l ].val + t[ t[ u ].r ].val - t[ u ].val ; 
        t[ t[ t[ u ].l ].l ].r = u , t[ t[ t[ u ].r ].r ].l = u ;//updata,更新左右编号
        t[ u ].l = t[ t[ u ].l ].l , t[ u ].r = t[ t[ u ].r ].r ;
        q.push( make_pair( t[ u ].val , u ) ) ;
        last = max( last , ans ) ;
    }
    cout<<last ;
}

如有不足,请大佬指出

转载于:https://www.cnblogs.com/ssw02/p/11528466.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值