序列单调递增的最小代价
题解
已知序列$a_1,a_2,…, a_n$,对于$a_i$,可以花费$b_i$的代价将$a_i$增加$1$,花费$c_i$的代价将$a_i$减小$1$,求将序列$a$变为非严格单调递增的最小代价。
最朴素的动态规划:设$f_{i,j}$表示只考虑前$i$个位置,且$a_i$的值变为$j$时的最小代价。
$$f_{i,j}=min_{k<=j}f_{i-1,k}+(a_i-j)*c_i \ (a_i\geq j)$$
$$f_{i,j}=min_{k<=j}f_{i-1,k}+(j-a_i)*b_i \ (a_i\leq j)$$
设$pre_{i,j}=min_{k<=j}f_{i,k}$
则上文中动态规划方程变为:
$$f_{i,j}=pre_{i-1,j}+(a_i-j)*c_i \ (a_i\geq j)$$
$$f_{i,j}=pre_{i-1,j}+(j-a_i)*b_i \ (a_i\leq j)$$
可以将$j$看作横坐标,$f_{i,j}$看为纵坐标,则通过归纳法(证明的过程实际就是下述维护函数的过程)可证明该函数是凹函数,斜率单调不降。
因此,可以将解题过程分为两步:
在函数中将$f_{i-1,j}$替换为$pre_{i-1,j}$,由于$f$为凹函数,因此可以通过在栈中维护函段将所有栈顶斜率大于$0$的线段替换为一个斜率等于$0$的线段。
将函数$pre_{i-1}$与函数$|a_i-j|*(a_i\geq j\ ?\ c_i\ : \ b_i)$(显然该函数为$V$形)求和,得到$f_i$。可以通过在栈中维护斜率变换处和斜率变化值来维护,显然将$pre$与$V$形函数求和只会在$a_i$处插入一个斜率变换值$b_i+c_i$。(注意同时要维护第一条线段的斜率和最后一条线段的斜率)
重复以上两步$n$次,得到$f_n$的函数图象,然后算出$f_{n,min { a_i}}$的函数值(显然,将所有$a_i$变为最小值即可),再通过函数图象递推出所有斜率变化处的函数值,取最小值即可得到答案。
代码
|