luogu P2893[USACO]修路Making the Grade (离散,dp)

luogu P2893 [USACO08FEB]修路Making the Grade

Description
农夫约翰想改造一条路,路有n(n<=2000)段,原来的路的每一段海拔是$A_i (A_i<=1e9)$,若修理后是$B_i$,会花费$|A_i – B_i|$,我们要求修好的路是单调不升或者单调不降的,求最小花费.

Hint
首先有一个结论:修改之后的每一段的高度肯定都是原来存在的高度.(感性证明就是为了保证花费最小,肯定要尽量让改后元素与改前相近)
至于具体为什么,我太菜了不知道….
所以实际上各个元素只有大小之分,我们把路的高度离散.这样不仅解决了$A_i$范围大的问题,dp数组大小也可以只开[2000][2000]
那么得出方程(以不下降为例)$$dp[i][j]=MIN^{j}_{k=1} {dp[i-1][k]+abs(B[j]-A[i])}$$
其中dp[i][j]表示第i段路高度取高度j(j为离散后的高度)时最小花费,B[i]表示离散高度为i的原高度
然而枚举k的话,时间复杂度为$n^3$.通过基本的dp优化思想,我们发现min(f[i-1][k])是可以在做第i-1段路段的时候处理出来的,所以复杂度就成了$n^2$.
不上升只要反着做一遍就好了

code

#include<iostream>
#include<cstdio>
#include<string>
#include<cmath>
#include<algorithm>
#define siz 2010
#define minn(a,b) (a<b?a:b)
using namespace std;
int n,ans;
int a[siz],b[siz],dp[siz][siz],mindp[siz][siz];
int main() {

    scanf("%d",&n);
    for(int i=1;i<=n;++i) scanf("%d",&a[i]),b[i]=a[i]; //说是hash,其实只要把b数组排序就好了
    sort(b+1,b+1+n);
    for (int i=1;i<=n;++i)
     for (int j=1;j<=n;++j) {
         dp[i][j]=mindp[i-1][j]+abs(a[i]-b[j]);
         if (j!=1) mindp[i][j]=minn(mindp[i][j-1],dp[i][j]);
         else mindp[i][j]=dp[i][j];
    }
    ans=mindp[n][n];
    for (int i=n;i;--i)
     for (int j=n;j;--j) {
         dp[i][j]=mindp[i+1][j]+abs(a[i]-b[j]);
         if (j!=n) mindp[i][j]=minn(mindp[i][j+1],dp[i][j]);
         else mindp[i][j]=dp[i][j];
     }
    ans=minn(ans,mindp[1][1]);
    printf("%d\n",ans);
    return 0;
}

afterword
这是一道双倍经验题,CodeForces 13C,它们一模一样.

猜你喜欢

转载自www.cnblogs.com/LonelyRyan/p/9036999.html