洛谷P1484.种树
题目解析及思路
题目要求在一条n个坑的路上,对于已知每个坑种树的收益,并且相邻两个坑不能同时种树的情况下,求最大收益
思考一个小范围的例子(不考虑数组全负数):
- 当m = 1时,答案一定为数组中的最大数,假设为a[i]
- 当m = 2时,答案有两种情况:1.仍取a[i],且再取一个其他不相邻的元素
2.不取a[i],改为取a[i-1]和a[i+1](同取或同不取)
因此可以用一个堆将所有数据加入,如果k=1时最优解为a[i],那么我们便可以把a[i-1]和a[i+1]进行合并
同时将a[i]改成a[i-1]+a[i+1]-a[i],继续找最大的,直到最大值<=0或取完k个
**反悔贪心:**因为a[i-1]+a[i+1] > a[i]时,我们要取a[i-1]和a[i+1]而不是a[i],因此在改为a[i-1]+a[i+1]-a[i]时,如果再取到a[i-1]+a[i+1]-a[i],a[i-1]+a[i+1]-a[i] + a[i] = a[i-1]+a[i+1] 可以实现找更优情况
参考题解
代码
#include<bits/stdc++.h>using namespace std;
#define PII pair<int, int>
typedef long long LL;
const int N = 300010;bool st[N];
//lr分别存左右两边的下标,更新方式参考链表
int l[N],r[N],a[N];
//优先队列存对组,记录下标
priority_queue<PII> q;
LL n,m,res;int main()
{cin>>n>>m;for(int i=1;i<=n;i++){//优先队列存对组,记录下标scanf("%d", &a[i]);q.push(make_pair(a[i],i));//记录左右下标l[i] = i-1,r[i] = i+1;}//哨兵r[0] = 1,l[n+1] = n;while(m --){//左右两边的元素被去掉了不遍历while(st[q.top().second])q.pop();PII tmp = q.top();q.pop();if(tmp.first <= 0) break;res += tmp.first;//取当前下标int pos = tmp.second;//把反悔值改进来a[pos] = a[l[pos]] + a[r[pos]] - a[pos];tmp.first = a[pos];//把两边去掉,标记成truest[l[pos]] = st[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<<res<<endl;
}