zoukankan      html  css  js  c++  java
  • 路面修整的题解

    FJ 打算好好修一下农场中某条凹凸不平的土路。按奶牛们的要求,修好后的
    路面高度应当单调上升或单调下降,也就是说,高度上升与高度下降的路段不能
    同时出现在修好的路中。
    整条路被分成了 N 段,N 个整数 A_1, … , A_N (1 <= N <= 2,000)依次描述
    了每一段路的高度(0 <= A_i <= 1,000,000,000)。FJ 希望找到一个恰好含 N 个
    元素的不上升或不下降序列 B_1, … , B_N,作为修过的路中每个路段的高度。
    由于将每一段路垫高或挖低一个单位的花费相同,修路的总支出可以表示为:
    |A_1 - B_1| + |A_2 - B_2| + … + |A_N - B_N|
    请你计算一下,FJ 在这项工程上的最小支出是多少。FJ 向你保证,这个支出
    不会超过 2^31-1。

    重点来了!!!敲黑板!!!

    首先,我们需要一个性质。这个性质是最终的所有数都源自于原来的数列。

    为什么?首先,我们证明一下。

    我们从 33 个数开始想,假设这 33 个数是 a,b,ca,b,c

    1. a<b&b<c&a<ca<b And b<c And a<c
    • 这种情况,比方说 7 8 9,之后,我们的方案是 7 8 9 是最优方案之一。
    1. a>b&b>c&a>ca>b And b>c And a>c
    • 这种情况,比方说 7 6 5,之后,我们的方案是 7 6 5 是最优方案之一。
    1. a<b&b>c&a<ca<b And b>c And a<c
    • 这种情况,比方说 1 9 7,之后,我们的方案是 1 7 9 是最优方案之一。
    1. a<b&b>c&a>ca<b And b>c And a>c
    • 这种情况,比方说 8 9 7,之后,我们的方案是 9 9 7 是最优方案之一。
    1. a>b&b<c&a>ca>b And b<c And a>c
    • 这种情况,比方说 7 5 9,之后,我们的方案是 5 5 9 是最优方案之一。
    1. a>b&b<c&a<ca>b And b<c And a<c
    • 这种情况,比方说 7 5 6,之后,我们的方案是 7 5 5 是最优方案之一。

    之后,我们考虑给数列加一个数 dd。我们刚才已经证明了,a,b,ca,b,c 3个数,任意排列都是最优的。现在,我们来分类讨论。

    为了方便,假设都是第 11 种情况,a<b<ca < b < c,最优排列是 a,b,ca,b,c

    • dad leq a

    这种情况,显然,对不对。比如 a,b,ca,b,c 的最优排列是 3,5,7,93,5,7,9d=2d=2。我觉得不用解释了。

    • da&dbdgeq a And dleq b

    这种情况,显然,对不对。比如 a,b,ca,b,c 的最优排列是 3,5,7,93,5,7,9d=4d=4。我觉得不用解释了。

    • db&dcdgeq b And dleq c

    这种情况,显然,对不对。比如 a,b,ca,b,c 的最优排列是 3,5,7,93,5,7,9d=6d=6。我觉得不用解释了。

    • dcdgeq c

    这种情况,显然,对不对。比如 a,b,ca,b,c 的最优排列是 3,5,7,93,5,7,9d=10d=10。我觉得不用解释了。

    竟然打了 44 个显然,只不过确实显然。

    然后,如果您就可以感性理解一下,我们继续加数,肯定依然是最优。

    这样代码就很好写了。

    大概这样:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    template<typename T>inline void read(T &FF){
        T RR=1;FF=0;char CH=getchar();
        for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
        for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
        FF*=RR;
    }
    template<typename T>inline void write(T x){
        if(x<0)putchar('-'),x*=-1;
        if(x>9)write(x/10);
        putchar(x%10+48);
    }
    template<typename T>inline void writen(T x){
        write(x);
        puts("");
    }
    const int MAXN=2e3+10;
    int n,a[MAXN],b[MAXN],ans,f[MAXN][MAXN];
    bool cmp1(int x,int y){
        return x>y;
    }
    bool cmp2(int x,int y){
        return x<y;
    }
    int getans(){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                f[i][j]=INT_MAX;
                for(int k=1;k<=j;k++)
    				f[i][j]=min(f[i][j],f[i-1][k]+abs(b[i]-a[j]));
            }
        }
        int ans=INT_MAX;
        for(int i=1;i<=n;i++)ans=min(ans,f[n][i]);
        return ans;
    }
    int main(){
        read(n);
        for(int i=1;i<=n;i++)read(a[i]),b[i]=a[i];
        sort(a+1,a+n+1,cmp1);
        ans=getans();
        sort(a+1,a+n+1,cmp2);
        ans=min(ans,getans());
        cout<<ans;
        return 0;
    }
    

    您会发现这个算法复杂度是 O(n3)O(n^3) 的,所以,还需要优化。我们发现找最小值,会有很多的重复计算

    所以,我们可以把这个算法,优化成 O(n2)O(n^2)

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    template<typename T>inline void read(T &FF){
        T RR=1;FF=0;char CH=getchar();
        for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
        for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
        FF*=RR;
    }
    template<typename T>inline void write(T x){
        if(x<0)putchar('-'),x*=-1;
        if(x>9)write(x/10);
        putchar(x%10+48);
    }
    template<typename T>inline void writen(T x){
        write(x);
        puts("");
    }
    const int MAXN=2e3+10;
    int n,a[MAXN],b[MAXN],ans,f[MAXN][MAXN];
    bool cmp1(int x,int y){
        return x>y;
    }
    bool cmp2(int x,int y){
        return x<y;
    }
    int getans(){
        for(int i=1;i<=n;i++){
            int ans=INT_MAX;
            for(int j=1;j<=n;j++){
                ans=min(ans,f[i-1][j]);
                f[i][j]=ans+abs(b[i]-a[j]);
            }
        }
        int ans=INT_MAX;
        for(int i=1;i<=n;i++)ans=min(ans,f[n][i]);
        return ans;
    }
    int main(){
        read(n);
        for(int i=1;i<=n;i++)read(a[i]),b[i]=a[i];
        sort(a+1,a+n+1,cmp1);
        ans=getans();
        sort(a+1,a+n+1,cmp2);
        ans=min(ans,getans());
        cout<<ans;
        return 0;
    }
    
  • 相关阅读:
    2015年新的征程,我的博客开通啦!
    基于USB3.0、DP1.2的网络隔离数据安全传输方案
    USB OTG to PC USB API简介
    SMA2SATA、PCIe2SATA转换模块(也有叫:Sata Test Fixtures)
    SATA接口硬盘加密器
    SVN二次开发——让SVN、TSVN(TortoiseSVN)支持windows的访问控制模型、NTFS ADS(可选数据流、NTFS的安全属性)
    About USB Data Link Cable API
    蓝牙4.0BLE抓包(三) – 扫描请求和扫描响应
    nRF51822外设应用[2]:GPIOTE的应用-按键检测
    蓝牙4.0BLE抓包(二) – 广播包解析
  • 原文地址:https://www.cnblogs.com/zhaohaikun/p/12816935.html
Copyright © 2011-2022 走看看