P1484 种树 - 堆 - 贪心

27 篇文章 0 订阅
13 篇文章 0 订阅

这题想得脑阔疼。。。我只想到可以选一个点,或者不选这个点选其左右两个点的和

先来特殊情况,k=1, 然后k=2
可以发现由1到2的过程中,可以是一个本来被选的点,替换为他左右两边的点,收益增加了a[pos+1] + a[pos-1] - a[pos]
这个题是一个个选,直到选了k个,有种递推的感觉,先确定种了前面几个,再确定这一个该怎么种

然后我不会处理若有别的点也选上,并且影响到这个pos+1和pos-1的情况

事实上可以把这三个点缩在一起啊(当然不能提前缩,要在pos这个点被选时缩起来)
然后我若再选一个别的点,若这个别的点会影响到pos+1或pos-1,因为缩了点,pos+1和pos-1现在都在pos上,所以这个“别的点”就会让pos的值不可用,由于我们把值也缩在pos上了,直接导致这个pos替换为pos+1和pos-1不可用了,这也符合题意,并且更好写
但是还是有点难处理怎么缩点,缩点之后应当删除3个点,新建一个点,而原来其他位于这三个点左边最靠右的点,若我们选了这个点,这说明这三个点就不可选了,也就是新点不可选,所以要把原来位于左边最靠右的点的右边连到新点上,这就需要开几个数组模拟链表什么的了

堆的题拆点(一个点拆成两个,可以是等大的两个,对左右两边起一个桥梁作用)和缩点(删点)比较多

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int MAXN = 1000000 + 10;
typedef long long ll;
ll n,k,a[MAXN],ans,flg[MAXN],cnt,l[MAXN],r[MAXN]; 
bool fina;
struct node{
	int val, pos;
	bool operator < (const node & a) const {
		return val < a.val;
	}
};
priority_queue<node> q;
int main() {
	scanf("%lld%lld", &n, &k);
	for(int i=1; i<=n; i++) {
		scanf("%lld", &a[i]);
		l[i] = i-1;
		r[i] = i+1;
		q.push((node){a[i], i});
	}
	int tot = n;
	while(!q.empty()) {
		node now = q.top();
		q.pop();
		if(now.val <= 0) break;
		int pos = now.pos;
		if(flg[pos]) continue;
		if(++cnt > k) break;
		ans += now.val;
		a[++tot] = a[l[pos]] + a[r[pos]] - a[pos];
		flg[pos] = flg[r[pos]] = flg[l[pos]] = 1;
		l[tot] = l[l[pos]], r[tot] = r[r[pos]];
		r[l[tot]] = tot;
		l[r[tot]] = tot;
		q.push((node){a[tot], tot});
	} 
	printf("%lld", ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值