传送门
解题思路
第一眼的贪心策略:每次都选最大的。
但是——不正确!
因为选了第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 }