POJ3666 线性dp+维度优化

POJ3666 线性dp+维度优化

题面

POJ3666 题面

思路

首先是重要的归纳法寻求思路的思想,在满足S最小化的前提下,假设存在一种构造前k位序列B的方案,即已经构造 B 1 B 2 ⋯ B k B_1B_2\cdots B_k B1B2Bk,则对于第 B k + 1 B_{k+1} Bk+1位可以用如下的构造方法
1)如果 B k ≤ A k + 1 B_{k}\leq A_{k+1} BkAk+1,则令 B k + 1 = A k + 1 B_{k+1} = A_{k+1} Bk+1=Ak+1,满足单调性且原命题成立
2)如果 B k > A k + 1 B_{k}> A_{k+1} Bk>Ak+1,那么两种转移思路:
①如果只由 A k + 1 A_{k+1} Ak+1一位得出,取 B k + 1 = B k B_{k+1} = B_{k} Bk+1=Bk
②如果由前面 B j B j + 1 ⋯ B k B_j B_{j+1}\cdots B_k BjBj+1Bk这些位得出的话,可以由一维数轴求绝对值最值的方法,求得最值是当 B j = B j + 1 = ⋯ = B k B_j =B_{j+1}=\cdots= B_k Bj=Bj+1==Bk时,令这些值等于 A j A j + 1 ⋯ A k A_j A_{j+1}\cdots A_k AjAj+1Ak的中位数
这里注意到, B i B_i Bi都是A中的元素
思路一:
只用一维表示,即 d p [ i ] dp[i] dp[i]表示完成前i个数的构造S的最小值,则有转移方程
d p [ i ] = m i n 0 ≤ j < i , A [ j ] ≤ A [ i ] { d p [ j ] + c o s t ( j + 1 , i − 1 ) } (1) dp[i] = min_{0\leq j<i,A[j]\leq A[i]}\lbrace dp[j]+cost(j+1,i-1) \rbrace \tag{1} dp[i]=min0j<i,A[j]A[i]{dp[j]+cost(j+1,i1)}(1)
其中cost(j+1,i-1)表示构造 B j B j + 1 ⋯ B k B_j B_{j+1}\cdots B_k BjBj+1Bk使得 A j ≤ B j ≤ B j + 1 ≤ ⋯ ≤ B k ≤ A i A_j\leq B_j\leq B_{j+1}\leq \cdots \leq B_k\leq A_i AjBjBj+1BkAi使得 ∑ k = j + 1 i − 1 ∣ A k − B k ∣ \sum_{k = j+1}^{i-1}{\mid A_k-B_k\mid} k=j+1i1AkBk的最小值
那么下面的工作就是求cost(j+1,i-1),要求整个cost数组,显然只能用朴素算法,遍历i和j,再用k遍历一层i和j之间的所有数 A k A_k Ak,求中位数,继而求 ∑ k = j + 1 i − 1 ∣ A k − B k ∣ \sum_{k = j+1}^{i-1}{\mid A_k-B_k\mid} k=j+1i1AkBk(没有考虑决策集合的优化,但是感觉可以优化)
因此这个复杂度为O( N 3 N^3 N3)
思路二:
注意到只把一个维度(已经处理的序列长度)作为“阶段”的要素,需要额外单独构造转移的序列,不足以实现转移,一般这样的情况需要考虑把另一个维度也放在状态里,一个直接的想法就是把B序列的最后一个值也记录在DP状态里面,设F[i,j]表示完成前i个数的构造,其中 B i = j B_i = j Bi=j,S的最小值,则转移方程为:
F [ i , j ] = m i n 0 ≤ k ≤ j { F [ i − 1 , k ] + ∣ A i − j ∣ } (2) F[i,j] = min_{0\leq k\leq j}\lbrace F[i-1,k]+\mid A_i-j \mid \rbrace \tag{2} F[i,j]=min0kj{F[i1,k]+Aij}(2)
由于j可能太大,因此需要离散化,把A中出现的数进行离散化,把DP状态中的第二维j的范围降低到O(N),另外,决策集合单调增(参照LCIS),O(1)即可实现转移,因此算法复杂度为O( N 2 N^2 N2)

注意事项

1)离散化的关键是,把原来的dp数组里面的一个维度的数,如本来应该为a[j]替换为j(把本来的1-n映射到a[1]-a[n]),即dp中的j存的是对应a[j]对应的值,用到的重要函数unique表示,”删除”序列中所有相邻的重复元素(只保留一个),并且返回数组的实际长度,正好满足离散化去重的目的

代码(思路二)

#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2005;
const ll INF = 2e9;
ll dp[maxn][maxn];
ll a[maxn],a2[maxn];
ll dis[maxn];
ll n,mini = INF,m;
ll abs1(ll a)
{
    if(a<0)return -a;
    return a;
}
void init()
{
    for(int i = 0;i <=n;i++)
        for(int j = 0;j <=n;j++)
            dp[i][j] = INF;
   for(int i = 0;i <=n;i++)
    dp[0][i] = 0;
    sort(a2,a2+n);
    m = unique(a2,a2+n)-a2;
}

int main()
{
    cin >> n;
    for(int i = 0;i <n;i++)cin >> a[i],a2[i] = a[i];
    init();
    for(int i = 1;i <=n+1;i++)
    {
        mini = dp[i-1][0];
        for(int j = 0;j <m;j++)
        {
            mini = min(dp[i-1][j],mini);
            if(i<=n)
            dp[i][j] = mini +abs1(a[i-1]-a2[j]);
        }
    }
    cout << mini << endl;

    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值