1. 程式人生 > >bzoj2151: 種樹(貪心+堆)

bzoj2151: 種樹(貪心+堆)

題目傳送門

解法:
今天才學這種經典做法。。
如果選最大的然後刪除兩邊很顯然這種策略是錯誤的。
因為有可能兩邊加起來更優。

那麼上面的做法是無法反悔的。。
給他一個反悔的機會。
那麼就是:
假設選了i,那麼刪除i前後的兩個點。
然後把i這個點的權值變為a[前]+a[後]-a[i]。
這樣再選i的話就表示反悔了選了前後兩個。
維護一下這個位置是否刪除。這個位置的前一個未被刪除的是哪個。後一個未被刪除的是哪個就行了。
具體看程式碼。

程式碼實現:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm> #include<cmath> #include<queue> #include<iostream> using namespace std; struct node { int w,c;node() {w=c=0;} friend bool operator <(node n1,node n2){return n1.c<n2.c;} };priority_queue<node> a; int q[210000],h[210000],n,m,s[210000];bool v[210000]; int
main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) {scanf("%d",&s[i]);q[i]=i-1;h[i]=i+1;node ss;ss.c=s[i];ss.w=i;a.push(ss);} if(m*2>n){printf("Error!\n");return 0;} q[1]=n;h[n]=1;memset(v,false,sizeof(v));int ans=0; for(int i=1;i<=m;i++) { while(v[a.top().w]==true)a.pop
(); node t=a.top();a.pop();ans+=t.c;int l=q[t.w],r=h[t.w]; node p;p.w=t.w;v[l]=true;p.c+=s[l];v[r]=true;p.c+=s[r];p.c-=t.c; s[t.w]=p.c;q[t.w]=q[l];h[q[l]]=t.w;h[t.w]=h[r];q[h[r]]=t.w;a.push(p); }printf("%d\n",ans); return 0; }