poj 3666 Making the Grade (动态规划)

来源:互联网 发布:贫困 知乎 编辑:程序博客网 时间:2024/06/01 10:38

题目链接;

题目大意:

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

(但是我看网上的代码均是将其变成单调不减序列  )

思路:DP

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

所以状态转移方程如下:

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

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

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

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

又从表格中可以看出:

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

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

然后对dp[i][j]=abs(a[i]-b[j])+mine;即可;

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


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

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

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


#include <stdio.h>#include <algorithm>#include <iostream>#include <string.h>using namespace std;#define maxn 2200long long  dp[maxn][maxn];int a[maxn],b[maxn];int n;void solve(){memset(dp,0,sizeof dp);for(int i=1;i<=n;i++){long long mine=dp[i-1][1];for(int j=1;j<=n;j++){mine=min(mine,dp[i-1][j]);dp[i][j]+=abs(a[i]-b[j])+mine;}}long long ans=dp[n][1];for(int i=1;i<=n;i++)ans=min(ans,dp[n][i]);printf("%lld\n",ans);}int main(){while(~scanf("%d",&n)){for(int i=1;i<=n;i++){scanf("%d",&a[i]);b[i]=a[i];}sort(b+1,b+1+n);solve();}    return 0;}

我们发现可知,二维dp每次只用当前行和上一行,所以没必要开二维数组,开一个一维数组dp记录当前行,一个一维数组pre记录上一行

#include <stdio.h>#include <algorithm>#include <iostream>#include <string.h>using namespace std;#define maxn 2200long long  dp[maxn],pre[maxn];int a[maxn],b[maxn];int n;void solve(){memset(dp,0,sizeof dp);memset(pre,0,sizeof pre);for(int i=1;i<=n;i++){long long mine=pre[1];for(int j=1;j<=n;j++){mine=min(mine,pre[j]);dp[j]=abs(a[i]-b[j])+mine;pre[j]=dp[j];}}long long ans=dp[1];for(int i=1;i<=n;i++)ans=min(ans,dp[i]);printf("%lld\n",ans);}int main(){while(~scanf("%d",&n)){for(int i=1;i<=n;i++){scanf("%d",&a[i]);b[i]=a[i];}sort(b+1,b+1+n);solve();}    return 0;}