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;
}

  

posted on 2012-08-20 17:18  夜->  阅读(355)  评论(0编辑  收藏  举报