POJ3666 Making the Grade [DP,离散化]

题意:

给定一个序列,以最小代价将其变成单调不增或单调不减序列,这里的代价看题目公式。

思路:

很容易想到是DP。

1.

对前i个序列,构成的最优解其实就是与两个参数有关。一个是这个序列处理后的最大值mx,和这个序列处理的代价值cost。

显然最大值mx最小最好(这样第i+1个值可以不花代价直接接在其后面的可能性更大),cost最小也最好(题意要求),但是两者往往是鱼和熊掌。

用dp[i][j]表示:前i个数构成的序列,这个序列最大值为j,dp[i][j]的值代表相应的cost。

所以状态转移方程如下:

dp[i][j]=abs(j-w[i])+min(dp[i-1][k]);(k<=j)

 

这个表格是根据转移方程写出来的dp数组。

再仔细看一下转移方程:dp[i][j]=abs(j-w[i])+min(dp[i-1][k]);(k<=j)

右边没填充的是因为填充的数字肯定比前面的数字大,无用,因为在求min( dp[i-1][k] )时,是求最小值,既然更大,则最小值时无需考虑。

又从表格中可以看出:

dp[i][j]=abs(j-w[i])+min(dp[i-1][k]);(k<=j)这里的k无需从1遍历到j。

只要在对j进行for循环的时候不断更新一个dp[i-1][j]的最小值mn=min(mn,dp[i-1][j]),

然后对dp[i][j]=abs(j-w[i])+mn即可;

这样改进之后即可从本来的时候时间复杂度O(NMM)改进为O(NM);

 

但是,这里的m是A[i]的最大值,显然TLE。

所以必须用离散化思想改进,因为N=2000。远小于A[i]的最大值。

离散化:将序列排序一下,然后用位置的前后关系来制定其值,这样时间复杂度变成O(N^2).

 

 

最后是这题数据有bug,只需要求不减序列即可。

 

 
  1. #include<iostream>

  2. #include<vector>

  3. #include<algorithm>

  4. #define Abs(a) ((a)>0?(a):-(a))

  5. #define Mod(a,b) (((a)-1+(b))%(b)+1)

  6. using namespace std;

  7. const int N=2005;

  8. const long long inf=(1<<60);

  9. int n;

  10. int a[N],b[N];

  11. long long int dp[N][N];

  12. void solve()

  13. {

  14. for(int i=1;i<=n;i++)

  15. {

  16. long long mn=dp[i-1][1];

  17. for(int j=1;j<=n;j++)

  18. {

  19. mn=min(mn,dp[i-1][j]);

  20. dp[i][j]=Abs(a[i]-b[j])+mn;

  21. }

  22. }

  23. long long ans=dp[n][1];

  24. for(int i=1;i<=n;i++)

  25. ans=min(ans,dp[n][i]);

  26. printf("%lld\n",ans);

  27. }

  28. int main()

  29. {

  30. scanf("%d",&n);

  31. for(int i=1;i<=n;i++)

  32. {

  33. scanf("%d",a+i);

  34. b[i]=a[i];

  35. }

  36. sort(b+1,b+n+1);

  37. solve();

  38.  
  39. return 0;

  40. }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值