http://poj.org/problem?id=3666
这是一道 练习左偏树的好题 不过很多人都是用动态规划做的
没看过左偏树的建议看看 课件之类的 不难理解 有一份ppt 上有原题
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<string> #include<vector> #include<set> #include<queue> #include<stack> #include<cmath> #define LL long long using namespace std; const int N=2005; struct node { int k,d; struct node *l,*r; }; struct node *head[N];//每个左偏树的根节点 大顶堆 int I; int l[N],r[N];//每个左偏树的左右范围 不过我们这里只是保存了中位数(如果有n个元素 中位数我们这里定义为第 n/2个数) 和小于中位数的数 int a[N]; void Left_tree_Merge(struct node *t1,struct node *t2)//将 t2指向的树合并到 t1 { struct node *t=t1->r; t1->r=t2; if(t==NULL) return; struct node *w=t1; while(w->r!=NULL&&w->r->k>t->k)//找到需要插入的地方 为 w的右孩子 w=w->r; if(w->r==NULL) {w->r=t;}//为空直接插入 else//否则递归合并 { struct node *temp=w->r; w->r=t; Left_tree_Merge(t,temp); } return ; } struct node * Left_tree_pop(struct node *t)//弹出堆顶元素 { if(t->l==NULL&&t->r!=NULL) {return t->r;} if(t->l!=NULL&&t->r==NULL) {return t->l;} if(t->l->k<t->r->k) swap(t->l,t->r); Left_tree_Merge(t->l,t->r); return t->l; } int update(struct node *t)//更新节点的 距离 维护左偏性 { if(t==NULL) return -1; int l1=update(t->l); int l2=update(t->r); t->d=min(l1,l2)+1; if(l1<l2) swap(t->l,t->r); return t->d; } int solve(int n) { int I=0; for(int i=0;i<n;++i,++I) { head[I]=new node; head[I]->d=0; head[I]->l=head[I]->r=NULL; head[I]->k=a[i]; l[I]=r[I]=i; while(I>0&&head[I-1]->k>=head[I]->k)//如果需要合并 { Left_tree_Merge(head[I-1],head[I]); update(head[I-1]);//合并后更新 if((r[I]-l[I]+1)%2==1&&(r[I-1]-l[I-1]+1)%2==1)//如果合并后元素过多 减少出一个 然后更新 { head[I-1]=Left_tree_pop(head[I-1]); update(head[I-1]); } r[I-1]=r[I];//合并区间 --I; } } int ans=0; for(int i=0;i<I;++i) { for(int j=l[i];j<=r[i];++j) { ans+=(abs(head[i]->k-a[j]));//累加 } } return ans; } int main() { //freopen("data.txt","r",stdin); int n; while(scanf("%d",&n)!=EOF) { for(int i=0;i<n;++i) scanf("%d",&a[i]);//先求非递减的解 int ans=solve(n); for(int i=0;i<n/2;++i) { swap(a[i],a[n-i-1]);//将数组逆序后 再求一边便是 非递加的了 } ans=min(ans,solve(n)); printf("%d\n",ans); } return 0; }