SJTU OJ 1282 修路 题解

1282. 修路

Description

蹦蹦跳跳结束后,cxt回头看看自己走过的路坑坑洼洼的,心中非常不爽,他表示要把这段路的路面高度修成单调上升的或者单调下降的,整条路可以看成 N段,N个整数A1,…..,An(1<=n<=2000)依次描述了每一段路的高度(0<=Ai<=1000000000)。 希望找到一个恰好含N个元素的不上升或不下降的序列B1,……,Bn,作为修过的路中每个路段的高度。

由于将每一段路垫高或挖低一个单位消耗的体力相同,于是可以表示为:

|A1-B1|+|A2-B2|+…..+|An-Bn|

请你计算一下,要修好这段道路,最少消耗多少体力。消耗的总体力不会超过2^31-1

Input Format

输入文件的第一行仅有一正整数N,以下的N行每行一个整数Ai,表示路面的高度。

Output Format

输出文件仅有一个正整数,表示如果把路修成高度不上升或不下降的最小花费

Sample Input

7
1
3
2
4
5
3
9

Sample Output

3

Hint

将第一个高度为3的路段的高度减少为2,将第二个高度为3的路段的高度增加到5,总花费为|2-3|+|5-3|=3,并且各路段的高度为一个不下降序列 1,2,2,4,5,5,9。


================================

题解正文

================================

  1. 题目分析

    1. 这是一道动态规划题目,求最小花费使道路变成不严格单调递增或不严格单调递减,数据大小用int即可。

    2. 修改后的值肯定是原来数列里的值。‘
    3. 对于结果来说,每一段都是不严格单调递增或不严格单调递减的数列。
  2. 解决方法
    1. 我们需要把一个数列排序(上升或下降都可以),来作为计算时的参考标准。(为什么需要这个标准呢?因为上面提到修改后的值肯定是原来粗线过的,而最终的结果是不严格单调的,所以与排序后的数列比较就很方便)
    2. 我们需要对输入的数列进行排序但不希望影响后来的操作,所以显然我们得把原数列复制一遍再排序。
    3. 朴素的思想就是我们依次把排序后的数列的某个数当作是我们最终修正后的路面的结束值(即最大或最小),因为当作参考的数列是排序过的,所以能保证结果满足题意,即不严格单调。
    4. 用f[i][j] 表示将原数列的前i个元素改为不严格单调且第i个元素改为排序后数列的第j个元素时所花费的最小的体力。用a表示原数列,b表示排序后的数列。得到状态转移方程f[i][j]=min{f[i-1][k]+abs(b[j]-a[i])}(1<=k<=j)
    5. 最后的结果就是从f[i][j]中找到最小值。
  3.     #include<iostream>
        #include<cmath>
        #include<algorithm>
        using namespace std;
         
        int n,ans=1<<30,a[2005],c[2005],f[2005][2005],g[2005][2005];
         
        void init()
        {
            cin>>n;
            for(int i = 1; i <= n ; ++i)
            {
                cin >> a[i];
                c[i]=a[i];
            }
            sort(c+1,c+n+1);
        }
        void sol()
        {
            for(int i = 1; i <= n; ++i)
            {
                for(int j = 1; j <= n; ++j)
                {
                    f[i][j] = g[i-1][j] + abs(a[i]-c[j]);
                    if(j == 1) g[i][j] = f[i][j];
                    else g[i][j] = min(f[i][j],g[i][j-1]);
                }
            }
            for(int i = 1; i <= n; ++i)
            ans = min(ans ,f[n][i]);
        }
         
        int main()
        {
            init();
            sol();//算一遍递增
            for(int i = 1; i <= n/2 ; ++i)
                swap(c[i],c[n-i+1]);
            sol();//算一遍递减
            cout<<ans;
        }



转载于:https://my.oschina.net/xueyang/blog/291324

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值