poj3666Making the Grade【dp离散化】

居然沦落到不到40行的dp还要翻题解再见你想咋的 这题可能唯一难就难在离散化做的少,就算告诉了状态转移方程也写不好,再说了,状态转移方程也不难啊,换到这个题就换了一个地方而已啊

方程:dp[i][j]=abs(j-a[i])+mn  i表示递推到哪位,j表示目前的最大值(这题数据只要满足不递减就可以了),mn表示前面一位的各个最大值时的花费最小值。然后离散化的思想是既然给的数据单个的值都那么大,那我莫不如排个序,用对应位置代替对应位置的数 。加油吧

p.s.时隔两日,每晚想此题久久不能入眠,深感领悟不够,遂除夕夜晚特来重温,并手写了这个表格,以下是新加的内容

关于为什么二重循环内部是当前这位的差值加的是mn,即当前大循环的最小值:

又写了二重循环出现最小值之后的部分才发现规律==对于大循环中的小循环来说,dp值得大小只可能是先下降,再增加(先考虑j表示值而不是离散化后的坐标的情况  j值最多不只是最大值)我们从原理上分析,在本次循环时。如果出现最小值的地方在上一轮出现最小值的后面,那没的说,我不可能用当前这位上次循环的dp值加上这次的差值,我肯定得找上次出现的最小值加本次的差值==因为上次出现的最小值的位置比这次小啊,我没必要让上一次非得加到我这次循环最小值那个位置,前一次比这一次小是可以的啊

/************
hdu3666
2016.2.5
31656K	157MS	C++	780B
************/
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define Abs(a) ((a)>0?(a):-(a))
using namespace std;
int a[2004],b[2004],n;
long long dp[2004][2004],mn;
int main()
{
  //  freopen("cin.txt","r",stdin);
    while(~scanf("%d",&n))
    {
        for(int i=1;i<=n;i++) {scanf("%d",&a[i]);b[i]=a[i];}
        sort(b+1,b+n+1);
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++)
        {
            mn=dp[i-1][1];
            for(int j=1;j<=n;j++)
            {
                mn=min(mn,dp[i-1][j]);
                dp[i][j]=Abs(a[i]-b[j])+mn;
            }
        }
        mn=dp[n][1];
        for(int i=1;i<=n;i++) if(mn>dp[n][i]) mn=dp[n][i];
        printf("%I64d\n",mn);
    }
    return 0;
}


你可能感兴趣的:(dp,poj)