• [codevs2572]路面修整


    题目描述 Description###

    (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)

    输入描述 Input Description###

    第一行包含一个正整数 (N) ,第二行包含 (N) 个正整数(A_i) ,两两之间用一个空格分隔。

    输出描述 Output Description###

    仅包含一个正整数,表示(FJ) 把路修成高度不上升或高度不下降的最小花费

    样例输入 Sample Input###

    7
    1 3 2 4 5 3 9
    

    样例输出 Sample Output###

    3
    

    数据范围及提示 Data Size & Hint###

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

    之前的一些废话###

    卸载了Rolling Sky与Dancing Line

    题解###

    我们可以贪心的发现若要改变一个数,一定会改成原序列的某个值,于是我们复制数组A到数组B然后对数组B进行排序。设(dp[i][j]) 表示([1,i]) 已经单调,并且第(i) 个数改成了(B) 数组第j个数的最小价值,转移的话十分暴力,(dp[i][j]=min exttt{{}dp[i-1][k]+abs(a_i-b_j) exttt{}}) ;其中保证(k<j) ,要不然数组就不单调了。很可惜复杂度是(O(n^3)) 的,会T飞的,所以我们尝试优化递推式,很显然是维护(dp[i-1][k]) ((k<j)) 中的最小值。这样我们就能做到(O(1)) 转移,本来我是开了一个新数组(M) 来维护最小值的,但是发现一直(WA) ,后来发现输出的(dp[n][n]) ,实际上应该是(M[n][n]) 。其实后来发现M数组和dp数组可以合到一起,最终复杂度(O(n^2))

    代码###

    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    typedef long long LL;
    #define mem(a,b) memset(a,b,sizeof(a))
    typedef pair<int,int> PII;
    inline int read()
    {
    	int x=0,f=1;char c=getchar();
    	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    	return x*f;
    }
    const int maxn=2010;
    int n,a[maxn],b[maxn],dp[maxn][maxn],M[maxn][maxn],ans;
    int main()
    {
    	n=read();
    	for(int i=1;i<=n;i++)a[i]=b[i]=read();
    	sort(b+1,b+n+1);
    	for(int i=1;i<=n;i++)
    	    for(int j=1;j<=n;j++)
    	    {
    	    	dp[i][j]=dp[i-1][j]+abs(a[i]-b[j]);
    	    	if(j>1)dp[i][j]=min(dp[i][j-1],dp[i][j]);
    	    }
    	ans=dp[n][n];
    	for(int i=1;i<=n/2;i++)swap(b[i],b[n-i+1]);
    	for(int i=1;i<=n;i++)
    	    for(int j=1;j<=n;j++)
    	    {
    	    	dp[i][j]=dp[i-1][j]+abs(a[i]-b[j]);
    	    	if(j>1)dp[i][j]=min(dp[i][j-1],dp[i][j]);
    	    }
    	printf("%d
    ",min(ans,dp[n][n]));
    	return 0;
    }
    

    总结###

    这道题自己没有想出来,因为当时想不出什么怎么设计状态,只有思考出“发现若要改变一个数,一定会改成原序列的某个值”才能进行DP,所以还是要多试几组样例来发现规律啊

  • 相关阅读:
    MySQL:procedure, function, cursor,handler
    Spring:ApplicationContext (2)
    Dojo: Quick Start
    Linux:常用命令
    Squirrel: 通用SQL、NoSQL客户端
    湖南师范大学邮箱申请及
    一文读懂基因测序技术的前世今生
    一 二 三代测序技术
    浅议基因测序技术的代际:后记
    浅议基因测序技术的代际
  • 原文地址:https://www.cnblogs.com/FYH-SSGSS/p/7694848.html
Copyright © 2020-2023  润新知