洛谷 P1484 种树(优先队列,贪心,链表)

传送门


解题思路

第一眼的贪心策略:每次都选最大的。

但是——不正确!

因为选了第i个树,第i-1和i-1棵树就不能选了。所以,要有一个反悔操作。

选了第i个后,我们就把a[i]的值更新为a[l[i]]+a[r[i]]-a[i]。

然后这样如果发现选i-1和i+1更优时,再次加上a[i],结果就变成了a[i]+a[l[i]]+a[r[i]]-a[i]=a[l[i]]+a[r[i]]。

然后这时再更新l[i]和r[i],把左边和右边两个节点删去。

因为每一次会比上一次多种一棵,所以循环k次,求一个ans即为答案。

也许会有疑问,当i-1或i+1是负数时,不选这个负数明显更优,但是a[i-1]+a[i+1]是包含了这个负数的。

其实我们考虑,当有一个是负数,且a[i-1]+a[i+1]>a[i]时,很显然是另一个正数大于a[i],而这个正数一定会比i这棵树先种,所以不必考虑这种情况。

最后,当现在操作的最大利益已经是负数或零了,可以直接break掉,因为树是最大k棵。

AC代码

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<queue>
 4 using namespace std;
 5 const int maxn=500005;
 6 int n,k,l[maxn],r[maxn],a[maxn],vis[maxn];
 7 long long ans;
 8 struct node{
 9     int id,value;
10     bool operator < (const node &x)const{
11         return value<x.value; 
12     }
13     node(int a,int b):id(a),value(b){}
14 };
15 priority_queue<node> q;
16 int main(){
17     cin>>n>>k;
18     for(int i=1;i<=n;i++){
19         scanf("%d",&a[i]);
20         l[i]=i-1;
21         r[i]=i+1;
22         q.push(node(i,a[i]));
23     }
24     r[0]=1;
25     l[n+1]=n;
26     while(k--){
27         while(vis[q.top().id]){
28             q.pop();
29         }
30         node x=q.top();
31         q.pop();
32         if(x.value<=0) break;
33         ans+=x.value;
34         int id=x.id;
35         vis[l[id]]=vis[r[id]]=1;
36         a[id]=a[l[id]]+a[r[id]]-a[id];
37         x.value=a[id];
38         r[l[l[id]]]=id;
39         l[r[r[id]]]=id;
40         l[id]=l[l[id]];
41         r[id]=r[r[id]];
42         q.push(x);
43     }
44     cout<<ans;
45     return 0;
46 }
posted @ 2019-12-13 21:08  尹昱钦  阅读(254)  评论(0编辑  收藏  举报