poj 3666 Making the Grade

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;

}

  

你可能感兴趣的:(poj)