POJ - 3666 Making the Grade(离散)

题目链接

题意:

给你n个整数a1,…,an(1≤n≤2000),描述沿道路的n个等距位置上的每个位置的路程(0≤ai≤100000000),从第一个场开始,到另一个场结束。FJ希望将这些高度调整为新的序列b1,。…,bn,即不增加或不减少。由于在道路上任何位置添加或清除污垢的成本相同,因此改造道路的总成本为| A 1 - B 1| + | A 2 - B 2| + … + | AN - BN |
请计算道路平整的最低成本,使之成为连续的斜坡。
(简单来讲就是将一串数列改为连续不递增或连续不递减)

思路 :

状态方程:dp[i][j]=abs(j-a[i])+min(dp[i-1][k]);(k<=j)

(i表示前i个数,j表示改变后第i个数的大小,a[i]原来i处的值)

  1. 根据状态方程可以求得时间复杂度O( n * m * m ),所以要降低时间复杂度,因为k<=j,所以我们可以将k处的循环归入j的循环中,并用一个temp维护最小值。这样时间复杂度缩小到O( n * m )。
  2. 状态方程变为:dp[i][j]=abs(j-a[i])+temp。再考虑j所要取的范围0 ≤ Ai ≤ 1,000,000,000,枚举j,还是超时的。
  3. 采用离散化的思想,很多空余的数不需要考虑,因为它们不在数列中。
  4. 每一次变成的新数一定在原序列中(可以用贪心的思想,例如 1,7 ,4,6,要改变4时,这一步花费最少,要让它非递减,则4改为7)。所以我们给原数列排个序,去重,得到b[i]。
  5. 状态方程变为dp[i][j]=abs(b[j]-a[i])+temp,此时dp[i][j]中j的含义为第i处的值变成第j小的数,时间复杂度降到O( n * n )

( * ^ ▽ ^ * )思路清晰之后代码就很好写了~如果要优化空间复杂度还可以使用滚动数组

#include<iostream>
#include<algorithm>
#include<math.h>
#include<cmath>
efine inf 0x3f3f3f3f
using namespace std;
int n;
int a[2010];
int b[2010];
int dp[2010][2010];
int main()
{
   scanf("%d",&n);
   for(int i=1;i<=n;i++)
   {
       scanf("%d",&a[i]);
       b[i]=a[i];
   }
   sort(b+1,b+n+1);
   for(int i=1;i<=n;i++)
   {
       int temp=inf;
       for(int j=1;j<=n;j++)
       {
           temp=min(temp,dp[i-1][j]);
           dp[i][j]=abs(a[i]-b[j])+temp;
       }
   }
   int ans=dp[n][1];
   for(int i=1;i<=n;i++)
   {
       ans=min(ans,dp[n][i]);
   }
   printf("%d",ans);
   return 0;
}

你可能感兴趣的:(线性dp)