Loading

P1484 种树 经典反悔贪心

P1484 种树 经典反悔贪心

题意

直线上有\(n\)个坑,这\(n\)个坑都可以种树,至多可以种\(k\)棵树,且不能在相邻的坑种树,每个坑位都有一个获利值,求怎样种可以让获利最大。

\[1 \leq n \leq 5e5\\ k \leq n / 2\\ -1e6 \leq w_i \leq 1e6 \]

分析

容易想到的DP做法:\(dp[i][j]\)表示前\(i\)棵树,种了\(j\)棵的最大获利。

\[dp[i][j] = max(dp[i - 1][j],dp[i - 2][j - 1] + w[i]) \]

这样的DP是\(n^2\)的,当然,据说可以用WQS二分来优化。

但是此题也是经典的反悔贪心:

容易想到,每三个位置,要么选中间的,要么选两边的,可是贪心的选择较大的不一定是最优解。

维护策略:维护一个大顶堆,每次取出一个点时,同时加入一个点,这个点的权值是左边权值+右边权值-当前权值,同时删除两边的点,(用双向链表维护即可)这样就让后悔机制自洽了。

复杂度O(nlogn)

代码

#include<bits/stdc++.h>
#define pii pair<ll,int>
#define eps 1e-7
#define equals(a,b) (fabs(a - b) < eps)
#define fi first
#define se second
using namespace std;
typedef long long ll;

const int maxn = 5e5 + 5;
const ll MOD = 1e9 + 7;

ll rd(){
	ll x = 0;
	int f = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9'){
		if(ch == '-')  f = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9'){
		x = x * 10 + ch - '0';
		ch = getchar();
	}
	return x * f;
}


struct P{
	ll val;
	int l,r;
};

P p[maxn];
bool vis[maxn];
priority_queue<pii> q;

void del(int x){
	p[x].l = p[p[x].l].l;
	p[x].r = p[p[x].r].r;
	p[p[x].l].r = x;
	p[p[x].r].l = x; 
}

int main(){
	int n = rd();
	int m = rd();
	for(int i = 1;i <= n;i++){
		p[i].val = rd();
		p[i].l = i - 1;
		p[i].r = i + 1;
		q.push(make_pair(p[i].val,i));
	}
	ll res = 0;
	while(m--){
		while(vis[q.top().se]) 
			q.pop();
		pii u = q.top();
		q.pop();
		if(u.fi <= 0) break;
		res += u.fi;
		vis[p[u.se].l] = vis[p[u.se].r] = 1;
		p[u.se].val = p[p[u.se].l].val + p[p[u.se].r].val - p[u.se].val;
		q.push(make_pair(p[u.se].val,u.se));
		del(u.se);
	}
	cout << res;
}
posted @ 2021-03-01 15:52  MQFLLY  阅读(92)  评论(0编辑  收藏  举报