POJ3666 线性dp+维度优化
题面
思路
首先是重要的归纳法寻求思路的思想,在满足S最小化的前提下,假设存在一种构造前k位序列B的方案,即已经构造
B
1
B
2
⋯
B
k
B_1B_2\cdots B_k
B1B2⋯Bk,则对于第
B
k
+
1
B_{k+1}
Bk+1位可以用如下的构造方法
1)如果
B
k
≤
A
k
+
1
B_{k}\leq A_{k+1}
Bk≤Ak+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+1⋯Bk这些位得出的话,可以由一维数轴求绝对值最值的方法,求得最值是当
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+1⋯Ak的中位数
这里注意到,
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]=min0≤j<i,A[j]≤A[i]{dp[j]+cost(j+1,i−1)}(1)
其中cost(j+1,i-1)表示构造
B
j
B
j
+
1
⋯
B
k
B_j B_{j+1}\cdots B_k
BjBj+1⋯Bk使得
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
Aj≤Bj≤Bj+1≤⋯≤Bk≤Ai使得
∑
k
=
j
+
1
i
−
1
∣
A
k
−
B
k
∣
\sum_{k = j+1}^{i-1}{\mid A_k-B_k\mid}
∑k=j+1i−1∣Ak−Bk∣的最小值
那么下面的工作就是求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+1i−1∣Ak−Bk∣(没有考虑决策集合的优化,但是感觉可以优化)
因此这个复杂度为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]=min0≤k≤j{F[i−1,k]+∣Ai−j∣}(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;
}