• 洛谷 P2893 [USACO08FEB]修路Making the Grade 解题报告


    P2893 [USACO08FEB]修路Making the Grade

    题目描述

    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

    |A1 - B1| + |A2 - B2| + ... + |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. 农夫约翰想改造一条路,原来的路的每一段海拔是A_i,修理后是B_i,花费|A_i – B_i|。我们要求修好的路是单调不升或者单调不降的。求最小花费。

    输入输出格式

    输入格式:

    • Line 1: A single integer: N

    • Lines 2..N+1: Line i+1 contains a single integer elevation: Ai

    输出格式:

    • 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.

    这个题,从各种意义上讲都不难,然而我没想出来没想出来。。。

    首先一定是要离散化的,因为不会去修一段没有出现的高度(可以证明这样不比最优解好)

    设离散化以后的坐标,(b[i])代表离散化以后排名为(i)的数字的值

    (dp[i][j])代表前(i)个组成的合法序列末尾元素的排名为(j)的最小花费

    则有转移(单调不降)
    (dp[i][j]=min_{k=1}^j(dp[i][k]+abs(a[i]-b[j])))

    显然可以前缀和优化一下子

    复杂度(O(N^2))


    Code:

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    const int N=2e3+10;
    int dp[N][N],g[N][N],n,a[N],b[N];
    int min(int x,int y){return x<y?x:y;}
    int max(int x,int y){return x>y?x:y;}
    int abs(int x){return x>0?x:-x;}
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",a+i),b[i]=a[i];
        std::sort(b+1,b+1+n);
        memset(g,0x3f,sizeof(g));
        memset(g[0],0,sizeof(g[0]));
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            {
                dp[i][j]=g[i-1][j]+abs(a[i]-b[j]);
                g[i][j]=min(g[i][j-1],dp[i][j]);
            }
        int ans=g[n][n];
        memset(g,0x3f,sizeof(g));
        memset(g[0],0,sizeof(g[0]));
        for(int i=1;i<=n;i++)
            for(int j=n;j;j--)
            {
                dp[i][j]=g[i-1][j]+abs(a[i]-b[j]);
                g[i][j]=min(g[i][j+1],dp[i][j]);
            }
        ans=min(ans,g[n][n]);
        printf("%d
    ",ans);
        return 0;
    }
    
    

    事实上有一种更加神奇的做法

    先说操作(单调非降的):
    从左到右,把当前位置的数放进大根堆,然后比较这个数和堆顶的大小。

    若比堆顶大,就不管

    若比堆顶小,就把堆顶拿出来变成这个数,然后答案增加堆顶与这个数的差

    代码大概是这样

    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a);
        q.push(a);
        if(a<q.top())
        {
            ans+=q.top()-a;
            q.pop();
            q.push(a);
        }
    }
    

    为什么捏?

    假设塞到第(i)了,前面是一个合法的递增序列,堆顶为(y),当前为(x)(x<y)

    这时候我们花掉了(y-x)块钱进行调整,考虑我们调整可以得到哪些结果

    二元组((x,x),(x+1,x+1),..(y-1,y-1),(y,y))都是可能的结果,虽然有的结果可能不合法,但一定存在合法的结果

    我们尽可能想让当前的数值小,所以我们尽可能会选择小的合法结果

    这时候我们发现,如果堆顶在后面被更新了,我们的合法结果的选择集合就变了

    如果我们直接把最小的可能不合法的结果放进堆,那么当比它大的元素都被砍掉后(也就是它成了堆顶),它就变得合法了


    2018.8.28

  • 相关阅读:
    data structure,(ADT)
    正交和投影的理解
    高阶函数(包含偏函数)简要介绍
    名字与对象之间的建立引用与解除引用,Python中不可修改对象等,换行加文档字符串,
    迭代器
    CrashCourse 笔记
    列表生成式的进化版——生成器以及iterator
    高代小笔记——关于共轭,欧式空间,双线性函数
    高等代数的笔记杂记——Jordan标准形,Jordan块
    Centos6版本搭建Cobbler,实现自动化部署多版本系统
  • 原文地址:https://www.cnblogs.com/butterflydew/p/9548407.html
Copyright © 2020-2023  润新知