斜率优化,把动规方程稍加变形,再用类似线性规划的方法思想进行优化,一般会用到单调队列优化等;
学习资料
题目
玩具装箱
题目传送门:luogu3195
题目
题目描述
P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。P教授有编号为
1
⋯
N
1\cdots N
1⋯N 的
N
N
N 件玩具,第
i
i
i件玩具经过压缩后变成一维长度为
C
i
C_i
Ci.为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的。同时如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第
i
i
i 件玩具到第
j
j
j 个玩具放到一个容器中,那么容器的长度将为
x
=
j
−
i
+
∑
k
=
i
j
C
k
x=j-i+\sum\limits_{k=i}^{j}C_k
x=j−i+k=i∑jCk制作容器的费用与容器的长度有关,根据教授研究,如果容器长度为 xx ,其制作费用为
(
X
−
L
)
2
(X-L)^2
(X−L)2.其中
L
L
L 是一个常量。P教授不关心容器的数目,他可以制作出任意长度的容器,甚至超过
L
L
L 。但他希望费用最小.
感谢@ACの666 提供的Latex题面
输入格式
第一行输入两个整数N,L.接下来N行输入
C
i
C_i
Ci.1<=N<=50000,1<=L,
C
i
C_ i
Ci<=10^7
输出格式
输出最小费用
输入输出样例
输入 #1
5 4
3
4
2
1
4
输出 #1
1
分析
设 d p [ i ] dp[i] dp[i] 为第 i i i 件物品放入时的最小花费(此时第 i i i 件物品是最后一个容器的最后一件物品)
设 j j j 为上一个容器的最后一个物品
则有动规方程:
d
p
[
i
]
=
m
i
n
(
d
p
[
j
]
+
[
s
u
m
[
i
]
−
s
u
m
[
j
]
+
i
−
(
j
+
1
)
−
L
]
2
)
dp[i] = min(dp[j] + [ sum[i] - sum[j]+i-(j+1)-L] ^2 )
dp[i]=min(dp[j]+[sum[i]−sum[j]+i−(j+1)−L]2)
拆开再分组:
d
p
[
i
]
=
m
i
n
(
d
p
[
j
]
+
[
s
u
m
[
i
]
+
i
−
(
s
u
m
[
j
]
+
j
)
−
(
1
+
L
)
]
2
)
dp[i] = min(dp[j] + [ sum[i] +i- (sum[j]+j)-(1+L)] ^2 )
dp[i]=min(dp[j]+[sum[i]+i−(sum[j]+j)−(1+L)]2)
我们先令
L
+
+
L++
L++
再设
a
[
i
]
=
s
u
m
[
i
]
+
i
a[i]=sum[i]+i
a[i]=sum[i]+i,
b
[
i
]
=
a
[
i
]
+
L
b[i]=a[i]+L
b[i]=a[i]+L
那么方程变为:
d
p
[
i
]
=
m
i
n
(
d
p
[
j
]
+
(
a
[
i
]
−
b
[
j
]
)
2
)
dp[i] = min(dp[j] + (a[i]- b[j]) ^2 )
dp[i]=min(dp[j]+(a[i]−b[j])2)
先不考虑
m
i
n
min
min,则:
d
p
[
i
]
=
d
p
[
j
]
+
(
a
[
i
]
−
b
[
j
]
)
2
dp[i] = dp[j] + (a[i]- b[j]) ^2
dp[i]=dp[j]+(a[i]−b[j])2
拆开:
d
p
[
i
]
=
d
p
[
j
]
+
a
[
i
]
2
−
2
a
[
i
]
b
[
j
]
+
b
[
j
]
2
dp[i] = dp[j] +a[i]^2 -2a[i]b[j]+b[j]^2
dp[i]=dp[j]+a[i]2−2a[i]b[j]+b[j]2
移项:
2
a
[
i
]
b
[
j
]
+
d
p
[
i
]
−
a
[
i
]
2
=
d
p
[
j
]
+
b
[
j
]
2
2a[i]b[j]+dp[i] -a[i]^2= dp[j]+b[j]^2
2a[i]b[j]+dp[i]−a[i]2=dp[j]+b[j]2
其中:
d
p
[
i
]
−
a
[
i
]
dp[i]-a[i]
dp[i]−a[i]不受
j
j
j的影响,
b
[
j
]
b[j]
b[j]受
j
j
j的直接影响,则:
设
b
[
j
]
=
x
b[j]=x
b[j]=x,
d
p
[
j
]
+
b
[
j
]
2
=
y
dp[j]+b[j]^2=y
dp[j]+b[j]2=y,
2
a
[
i
]
2a[i]
2a[i]为一次项系数,求
d
p
[
i
]
dp[i]
dp[i]的最小值
转化为线性规划问题。
y
=
2
a
[
i
]
x
+
d
p
[
i
]
−
a
[
i
]
2
y=2a[i]x+dp[i]-a[i]^2
y=2a[i]x+dp[i]−a[i]2
y
=
x
2
+
d
p
[
j
]
y=x^2+dp[j]
y=x2+dp[j]
想象一条斜率为
2
a
[
i
]
2a[i]
2a[i]的直线,在逼近二次函数
x
2
+
d
p
[
j
]
x^2+dp[j]
x2+dp[j]
注意到:
a
[
i
]
a[i]
a[i]随
i
i
i递增,所以答案的
j
j
j也是必定递增的
所以动规可以用单调队列优化为线性的。
又由于
d
p
[
j
]
dp[j]
dp[j]的不同,
y
y
y不一定是递增的
此时需要我们将队列中下降的部分除去,以保证队列中元素的斜率单调
代码
/*************************
User:Mandy.H.Y
Language:c++
Problem:luogu3195 Toy
Algorithm:斜率优化
Date:2019.7.24
Scores:
*************************/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e4 + 5;
int n,L;
long long a[maxn],b[maxn],dp[maxn];
long long l,r,q[maxn << 1];
template<class T>inline void read(T &x){
x = 0;bool flag = 0;char ch = getchar();
while( ! isdigit(ch)) flag |= ch == '-',ch = getchar();
while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
if(flag) x = -x;
}
template<class T>void putch(const T x){
if(x > 9) putch( x / 10);
putchar(x % 10 | 48);
}
template<class T>void put(const T x){
if(x < 0) putchar('-'),putch(-x);
else putch(x);
}
void file(){
freopen("3195.in","r",stdin);
freopen("3195.out","w",stdout);
}
void readdata(){
read(n);read(L);
++L;
for(int i = 1;i <= n; ++ i){
long long x;read(x);
a[i] = a[i - 1] + x;
}
for(int i = 1;i <= n; ++ i){
a[i] += i;
b[i] = a[i] + L;
}//i不能累加,要单独加上去
}
void work(){
l = r = 1;//保证队列中至少两个元素
b[0] = L;//初始化
for(int i = 1;i <= n; ++ i){
long long k = a[i] << 1;
while(l < r &&
(b[q[l + 1]] * b[q[l + 1]] + dp[q[l + 1]] - b[q[l]] * b[q[l]] - dp[q[l]]) <=
(k * (b[q[l + 1]] - b[q[l]])))
++ l;
dp[i] = dp[q[l]] + (a[i] - b[q[l]]) * (a[i] - b[q[l]]);
while(l < r &&
(dp[q[r]] + b[q[r]] * b[q[r]] - dp[q[r - 1]] - b[q[r - 1]] * b[q[r - 1]]) * (b[i] - b[q[r]]) >=
(dp[i] + b[i] * b[i] - dp[q[r]] - b[q[r]] * b[q[r]]) * (b[q[r]] - b[q[r - 1]]))
-- r;
q[++ r] = i;
}
put(dp[n]);
}
int main(){
// file();
readdata();
work();
return 0;
}