Making the Grade(POJ-3666)(DP求解,优化DP)

5 篇文章 0 订阅

前言

这道题我一开始用最长上升子序列做,结果WA了…但其实差不多,就是求解时要变一下.

题目

传送门
题目大意:
在一条土路上有n段(1<=n<=2000),每段都有一个高度 Ai(1<=i<=n) A i ( 1 <= i <= n ) ,现在你可以对每一段增加或减少一些土,使得这条路变成单调不上升或单调不下降,求改动土的最小值?

思路

首先,这道题数据有Bug,只需要求单调不下降序列就可以
好了,我们来看看怎么写这道题的DP?
我们就假设要凑不下降序列,那么对于一个位置i来说的话只有两点要考虑:
1.将i连着前面变成不下降的土的花费尽量少。
2.i的高度越低越好。
那我们可以用一个数组b来存储所有出现的数,这里是去重、从小到大排了序的
我们的目的就是对于i来说枚举所有b中元素,找到它对应的花费,那这样就可以进行DP了.
我们定义: f[i][j]:i,ib[j] f [ i ] [ j ] : 前 i 个 数 不 下 降 , 第 i 个 数 凑 成 b [ j ] 时 的 最 小 花 费
f[i][j]=minf[i1][k]+abs(a[i]b[j])(1<=i<=n1<=k<=j<=m) f [ i ] [ j ] = m i n f [ i − 1 ] [ k ] + a b s ( a [ i ] − b [ j ] ) ( 1 <= i <= n 1 <= k <= j <= m )
但我们发现这样写过后是 O(n3) O ( n 3 ) 的时间复杂度对于数据是过不了的,那我们要优化!
我们用m表示b数组的长度,由于j是递增的,k是不断从1~j,我们就算多了很多次,那我们可以用一个Min来记录1到当前j的 f[i1][k] f [ i − 1 ] [ k ] 的最小值.我们就优化成:
f[i][j]=Min+abs(a[i]b[j])(1<=i<=n1<=j<=m) f [ i ] [ j ] = M i n + a b s ( a [ i ] − b [ j ] ) ( 1 <= i <= n 1 <= j <= m )
那就可以过了~~
输出答案时注意不是直接输出 f[n][m] f [ n ] [ m ] ,而是输出 minf[n][i](1<=i<=m) m i n f [ n ] [ i ] ( 1 <= i <= m )

代码

#include<set>
#include<map>
#include<ctime>
#include<queue>
#include<cmath>
#include<cstdio>
#include<climits>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
int read(){
    int f=1,x=0;char s=getchar();   
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}  
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}
#define MAXN 2000
#define INF 0x3f3f3f3f
int f[MAXN+5][MAXN+5],a[MAXN+5],b[MAXN+5];
int main(){//前i个数以b[j]结尾的最小花费
    int n=read(),m;
    for(int i=1;i<=n;i++)
        a[i]=read(),b[i]=a[i];
    sort(b+1,b+n+1);//b数组排序
    m=unique(b+1,b+n+1)-(b+1);//这是一个去重函数
    for(int i=1;i<=n;i++){
        int Min=INF;//Min优化:f[i-1][k](1<=k<=j)中的最小值
        for(int j=1;j<=m;j++){
            Min=min(Min,f[i-1][j]);//更新最小值
            f[i][j]=Min+abs(a[i]-b[j]);//状态转移
        }
    }
    int ans=INF;//统计答案
    for(int i=1;i<=m;i++)
        ans=min(ans,f[n][i]);
    printf("%d\n",ans);
    return 0;
}

题外话

……如果真的改数据的话,我们把存一下当前的最优答案,然后把a数组倒过来,f数组清零,再做一遍就可以了,因为你求了a数组倒序的不下降序列的最小花费就是a数组正序的不上升序列的最小花费

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值