poj 3666 Making the Grade

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Littlewhite520/article/details/55040408

A straight dirt road connects two fields on FJ’s farm, but it changes elevation more than FJ would like. His cows do not mind climbing up or down a single slope, but they are not fond of an alternating succession of hills and valleys. FJ would like to add and remove dirt from the road so that it becomes one monotonic slope (either sloping up or down).

You are given N integers A1, … , AN (1 ≤ N ≤ 2,000) describing the elevation (0 ≤ Ai ≤ 1,000,000,000) at each of N equally-spaced positions along the road, starting at the first field and ending at the other. FJ would like to adjust these elevations to a new sequence B1, . … , BN that is either nonincreasing or nondecreasing. Since it costs the same amount of money to add or remove dirt at any position along the road, the total cost of modifying the road is

| A 1 - B 1| + | A 2 - B 2| + … + | AN - BN |
Please compute the minimum cost of grading his road so it becomes a continuous slope. FJ happily informs you that signed 32-bit integers can certainly be used to compute the answer.

Input
* Line 1: A single integer: N
* Lines 2..N+1: Line i+1 contains a single integer elevation: Ai

Output
* Line 1: A single integer that is the minimum cost for FJ to grade his dirt road so it becomes nonincreasing or nondecreasing in elevation.

Sample Input
7
1
3
2
4
5
3
9
Sample Output
3
http://blog.sina.com.cn/s/blog_82a8cba50100tog6.html
方法一:dp
转移方程就是 dp[i][j]=第i个数排在第j位时候的情况的值,前面i-1都已经排好了。 dp[i][j]=min(dp[i][1…j])+(a[i]-a[pos[j])) k 1~j.
其实就是把每个i数排在所有位置的情况都算了一遍。数据弱能成功。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
long long a[101000];
long long n[101000];
long long b[101000];
long long dp[2010][2010];
int main()
{
    int n;
    while(cin>>n)
    {
        memset(a,0,sizeof(a));
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
            b[i]=a[i];
        }
        sort(b+1,b+1+n);
        for(int j=1;j<=n;j++)
        {
            dp[1][j]=a[1]-b[j];
            if(dp[1][j]<0) 
                dp[1][j]=-dp[1][j];
        }
        long long t;
        for(int i=2;i<=n;i++)
        {
            long long k=dp[(i+1)%2][1];
            for(int j=1;j<=n;j++)
            {
                k=min(dp[(i+1)%2][j],k);
                t=a[i]-b[j];
                if(t<0) t=-t;
                dp[i%2][j]=k+t;
            }
        }
        long long ans=dp[n%2][n];
        for(int i=n;i>=1;i--)
            ans=min(ans,dp[n%2][i]);
        printf("%lld\n",ans );
        return 0;
    }
}

方法二 :左式堆
http://wenku.baidu.com/link?url=RFeAhCglece9nXK2l46PDRlOXGU7XigI3x6ANeaZHBl9-xf-haWsn0siBZ3qcXAdylvGBUukc2rqwafaE_tZzy9aaok7oPNG65oky-oUcCu

左式堆在合并上有很大优势
可以达到 o(logn)
在本题上合并的基本步骤
材料 一个结构体数组, 三个int数组 一个longlong 答案数组。
步骤一:for while 不是第一个 而且比上一个树根的小,那么合并。上一个树根的平均值比当前树根大,会一直合并,直到比当前小于等于。记录和合并后树的长度。
在merge合并里面 判断右左子树是否为空,令树根为较大值,用树根的右子树与值小的y合并。更新lr的长度。更新树根长度 树根等于右子树+1

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define MAXN 2010
int N,v[MAXN],len[MAXN],stk[MAXN];
long long  ans[5];
struct Node
{
    int v,l,r,dis;
    Node(){}
    Node(int _v,int _l,int _r,int _d):
    v(_v),l(_l),r(_r),dis(_d) {}
}nn[2][MAXN];

int merge(Node n[],int x,int y) //在这里 x是右,首先右子树为空直接返回子树。
{
    if(!x) return y;
    if(!y) return x;
    if(n[x].v<n[y].v) swap(x,y);  //y是小的那个,树根的大的
    n[x].r=merge(n,n[x].r,y); //用树根的右子树与值小的y合并
    if(n[n[x].l].dis<n[n[x].r].dis) swap(n[x].l,n[x].r); //更新l长度。
    n[x].dis=n[n[x].r].dis+1; //更新树根长度 树根等于右子树+1
    return x;
}
void solve(Node n[],int t)
{
    int top=0;
    for(int i=0;i<N;i++)
    {
        int ct=1;int id=i;
        while(top>0&&n[stk[top-1]].v>n[id].v) //如果上一个树根的平均值大于当前的,要进行合并
        { //当上一个树根的平均值比当前树根大,会一直合并,直到比当前小于等于。
            top--;
            id=merge(n,stk[top],id); //合并  上一棵树和当前这课树 第一棵树不用合并
            if((len[top]+1)/2+(ct+1)/2>(len[top]+ct+1)/2)
                id = merge(n,n[id].l,n[id].r); //如果结点多了一个 那么删掉最大点 也就是树根
            ct+=len[top];
        }
        len[top]=ct;   
        stk[top++]=id;  //保留前面所有正确有序的树根结点。
    }
    for(int i=0,j=0;i<top;i++)
    {
        int k=n[stk[i]].v;
        while(len[i]--) ans[t]+=abs(v[j++]-k);  //更新结果
    }

}
int main()
{
    while(~scanf("%d",&N))
    {
        memset(len,0,sizeof(len));
        memset(nn,0,sizeof(nn));
        for(int i=0;i<N;i++)
        {
            cin>>v[i];
            nn[0][i]=nn[1][N-i+1]=Node(v[i],0,0,0);
        }
        ans[0]=ans[1]=0;
        for(int i=0;i<2;i++) solve(nn[i],i);
            printf("%lld\n",min(ans[0],ans[1]));
    }
}
展开阅读全文

没有更多推荐了,返回首页