bzoj2151: 种树
思路很妙啊。
我一开始就在想DP,但是时间必挂。
膜了题解才发现原来是贪心。首先如果没有限制左右不能选,那就很简单,那现在的做法就是给他一个反悔的机会
比如数列9 10 8 -1
那贪心一开始就选10,这时把数列变成:9+8-10 -1 = 7 -1
那么下一次选的就是7,相当于减的10抵消而选了9、8
那么用链表维护左右,用优先队列排序就行了。
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> #include<queue> using namespace std; struct node { int id,d; }a[210000]; struct cmd { bool operator()(node n1,node n2) { return (n1.d==n2.d)?(n1.id<n2.id):(n1.d<n2.d); } }; priority_queue<node,vector<node>,cmd>q,d; int c[210000],l[210000],r[210000]; int main() { int n,m; scanf("%d%d",&n,&m); if(n/2<m){printf("Error!\n");return 0;} for(int i=1;i<=n;i++) { scanf("%d",&c[i]); a[i].id=i;a[i].d=c[i];q.push(a[i]); l[i]=i-1;if(l[i]==0)l[i]=n; r[i]=i+1;if(r[i]==n+1)r[i]=1; } int ans=0; for(int i=1;i<=m;i++) { node n1,n2; while(1) { n1=q.top(); if(d.empty()==true){q.pop();break;} n2=d.top(); if(n1.d==n2.d&&n1.id==n2.id) { q.pop();n1=q.top(); if(d.empty()==true)break; d.pop();n2=d.top(); } else {q.pop();break;} } ans+=n1.d; int L=l[n1.id],R=r[n1.id]; node de; de.id=L,de.d=c[L];d.push(de); de.id=R,de.d=c[R];d.push(de); node in; in.id=n1.id,in.d=c[L]+c[R]-n1.d;q.push(in); c[n1.id]=c[L]+c[R]-n1.d; l[n1.id]=l[L];r[l[L]]=n1.id; r[n1.id]=r[R];l[r[R]]=n1.id; } printf("%d\n",ans); return 0; }
pain and happy in the cruel world.