Esta serie es una extensión de este libro de texto de algoritmos: "Introducción al concurso de algoritmos avanzados" ( Jingdong Dangdang ) Tsinghua University Press
Si tiene alguna sugerencia, comuníquese con: (1) QQ Group, 567554289; (2) Autor QQ, 15512356
Hay una clase de ecuaciones de estado DP, por ejemplo:
\ (dp [i] = min \ {dp [j] -a [i] * d [j] \} \) \ (0≤j <i, d [j] ≤d [j + 1], a [
i] ≤ a [i + 1] \) que se caracteriza por la presencia tanto de un \ (I \) tener \ (J \) término \ (a [i] * d [ j] \) .
Al programar, si simplemente recorre \ (i \) y \ (j \) , la complejidad es \ (O (n ^ 2) \) .
Mediante la optimización de la pendiente (truco del casco convexo, optimización del casco convexo), la complejidad del tiempo se optimiza a \ (O (n) \) .
La tecnología central de la optimización de taludes es el modelo de taludes (casco convexo) y la cola monótona.
1. Transforma la ecuación de estado en un problema de pendiente plana
La ecuación encuentra el valor óptimo de \ (dp [i] \) cuando \ (j \) cambia para un \ (i \) fijo , por lo que la parte sobre \ (i \) puede considerarse como un valor fijo, y La parte sobre \ (j \) se considera como una variable. El \ (min \) se retira, la ecuación en: \ (DP [J] = a [i] * D [J] + DP [I] \) para la conveniencia de la observación, de modo que: \ (Y = DP [J] \ ) , \ (X = d [j] \) , \ (k = a [i] \) , \ (b = dp [i] \) , la ecuación se convierte en: \ (y = kx + b \) optimización de la pendiente El modelo matemático de es convertir la ecuación de transferencia de estado en una línea recta en el sistema de coordenadas planas: \ (y = kx + b \) . Entre ellos: (1) Las variables \ (x \) , \ (y \) y \ (j \) están relacionadas, y solo \ (y \) contiene \ (dp [j] \) . El punto \ ((x, y) \) es una posible decisión en la pregunta.
(2) La pendiente \ (k \) y la intersección \ (b \) están relacionadas con \ (i \) , y solo \ (b \) contiene \ (dp [i] \) . El \ (b \) más pequeño contiene el \ (dp [i] \) más pequeño , que es la solución de la ecuación de estado.
Tenga en cuenta las 2 condiciones para aplicar la optimización de la pendiente: \ (x \) y \ (k \) aumentan de forma monótona, es decir, \ (x \) aumenta con \ (j \) , \ (k \) sigue \ (i \) incrementos e incrementos.
2. Encuentra un dp [i]
Considere primero encontrar \ (dp [i] \) cuando esté fijo \ (i \) . Como \ (i \) es un valor fijo, la pendiente \ (k = a [i] \) puede considerarse como una constante. Cuando \ (J \) en \ (0≤j <i \) cuando el cambio de un \ (j_r \) , la generación de un punto \ (V_R = (x_r, Y_r) \) , en este punto en una línea recta \ (y = kx + b_r \) , \ (b_r \) es la intersección. Como se muestra en la Figura 1.
![](https://img-blog.csdnimg.cn/20200416161837383.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkxNDU5Mw==,size_16,color_FFFFFF,t_70)
Para \ (0≤j <i \) en todo \ (J \) , a sus puntos correspondientes se dibujan en el plano, la pendiente de la línea que corresponde al punto \ (k = a [i] \) son los mismos, Solo la intersección \ (b \) es diferente. Entre todos estos puntos, hay un punto \ (v '\) donde la línea recta tiene la menor intersección \ (b' \) , calcular \ (b '\) , porque \ (b' \) contiene \ (dp [ i] \) , entonces se calcula el óptimo \ (dp [i] \) . Como se muestra en la Figura 2.
![](https://img-blog.csdnimg.cn/20200416162329969.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkxNDU5Mw==,size_16,color_FFFFFF,t_70)
¿Cómo encontrar el \ (v '\) más ventajoso ? Use el "caparazón convexo inferior".
Como se mencionó anteriormente, \ (x \) aumenta monotónicamente, es decir, \ (x \) aumenta con \ (j \) . En la Figura 3 (1) se dan cuatro puntos, y sus coordenadas \ (x \) están aumentando.
![](https://img-blog.csdnimg.cn/20200416162600735.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkxNDU5Mw==,size_16,color_FFFFFF,t_70)
1, 2 y 3 en la figura 3 (1) constituyen un "casco convexo inferior". La característica del "casco convexo inferior" es que la pendiente del segmento de línea 12 es más pequeña que la pendiente del segmento de línea 23. 2, 3, 4 constituyen la "carcasa convexa superior". La intersección b de la línea recta que pasa por el punto medio 3 del casco convexo superior debe ser más pequeña que la intersección de la línea recta que pasa por 2 o 4 con la misma pendiente, por lo que el punto 3 definitivamente no es la mejor ventaja.
Después de quitar el "casco convexo superior", obtenemos la Figura 3 (2), y todos los puntos restantes satisfacen la relación del "casco convexo inferior". La mejor ventaja radica en la "carcasa convexa inferior". Por ejemplo, en la Figura 3 (3), corte estos puntos con una línea recta con una pendiente de \ (k \) . Si la pendiente del segmento de línea 12 es menor que \ (k \) y la pendiente de 24 es mayor que \ (k \) , entonces el punto 2 La mayor ventaja de la "carcasa convexa inferior".
La operación anterior es muy conveniente para usar la programación de cola monótona.
(1) Entrar al equipo, manteniendo un "casco convexo inferior" en la cola, es decir, una línea recta compuesta por cada 2 puntos consecutivos, cuya pendiente está aumentando de manera monótona. Cuando se ingresa un nuevo punto en la cola, asegúrese de que todavía pueda formar un "casco convexo inferior" junto con los puntos en la cola. Por ejemplo, los dos puntos al final de la cola son \ (v_1 \) , \ (v_2 \) , y el nuevo punto que se agregará a la cola es \ (v_3 \) . Compare \ (v_1 \) , \ (v_2 \) , \ (v_3 \) para ver si las pendientes de los segmentos de línea \ (v_1v_2 \) y \ (v_2v_3 \) aumentan, en caso afirmativo, entonces \ (v_1 \) , \ (v_2 \) ,\ (v_3 \) forma un "casco convexo inferior"; si la pendiente no aumenta, significa que \ (v_2 \) está mal, bótela en la cola del equipo; luego continúe comparando los 2 puntos en la cola de la cola y \ (v_3 \) ; repita La operación anterior hasta \ (v_3 \) puede ingresar al equipo. Después de las operaciones anteriores, los puntos en la cola forman un gran "casco convexo inferior". La línea recta formada por cada 2 puntos tiene una pendiente creciente, y la cola sigue siendo una cola monótona.
(2) Sal de la cola y encuentra la mejor ventaja. Los dos puntos al frente del equipo son \ (v_1 \) y \ (v_2 \) . Si la pendiente del segmento de línea \ (v_1v_2 \) es menor que \ (k \) , significa que \ (v_1 \) no es la mejor ventaja. , Continúe comparando los dos nuevos puntos en la cabeza del equipo hasta que la pendiente sea mayor que \ (k \) , luego el punto en la cabeza del equipo es el más ventajoso \ (v '\) .
3. Encuentra todos los dp [i]
Lo anterior obtuvo a \ (dp [i] \) , complejidad \ (O (n) \) . Si todo \ (I \) , cada solicitud \ (DP [I] \) , la complejidad global sigue siendo \ (O (n ^ 2) \) , y la complejidad computacional no cambia. ¿Hay un método optimizado?
Un \ (i_1 \) más pequeño , su punto correspondiente es { \ (v_0, v_1, ..., v_ {i1} \) }; un \ (i_2 \) más grande corresponde a más puntos { \ (v_0, v_1, ..., v_ {i1}, ..., v_ {i2} \) }, que contiene todos los puntos de \ (i_1 \) . Cuando busque la mejor ventaja de \ (i_1 \) , debe marcar { \ (v_0, v_1, ..., v_ {i1} \) }; cuando busque la mejor ventaja de i2, debe marcar { \ (v_0, v_1 ,. .., v_ {i1}, ..., v_ {i2} \) }. Aquí se realizan verificaciones duplicadas y se pueden evitar estas duplicaciones. Aquí es donde se puede optimizar, y aún usar el "shell convexo inferior" para la optimización.
(1) La pendiente correspondiente a cada \ (i \) \ (k_i = a [i] \) es diferente, de acuerdo con las restricciones\ (a [i] ≤ a [i + 1] \) , cuando \ (i \) aumenta, la pendiente aumenta.
![](https://img-blog.csdnimg.cn/20200416163451688.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkxNDU5Mw==,size_16,color_FFFFFF,t_70)
(2) Como se mencionó anteriormente, cuando encuentre el mejor punto de a \ (i_1 \) , puede eliminar algunos puntos, es decir, aquellos con una pendiente menor que \ (k_ {i1} \) . Estos puntos eliminados, cuando el \ (i_2 \) más grande en la parte posterior , porque la pendiente \ (k_ {i2} \) también es más grande, deben eliminarse.
De acuerdo con la discusión de (1) y (2), el método de optimización es: para todos \ (i \) , use una cola monótona para manejar todos los puntos de manera unificada; los puntos eliminados por el \ (i_1 \) más pequeño están en cola monotónica Rebote, el \ (i_2 \) más grande en la parte posterior ya no los procesa.
Como cada punto solo ingresa a la cola monótona una vez, la complejidad total es \ (O (n) \) .
El siguiente código demuestra la operación anterior.
//q[]是单调队列,head指向队首,tail指向队尾,slope()计算2个点组成的直线的斜率
for(int i=1;i<=n;i++){
while(head<tail && slope(q[head],q[head+1])<k) //队头的2个点斜率小于k
head++; //不合格,从队头弹出
int j = q[head]; //队头是最优点
dp[i] = ...; //计算dp[i]
while(head<tail && slope(i,q[tail-1])<slope(q[tail-1],q[tail])) //进队操作
tail--; //弹走队尾不合格的点
q[++tail] = i; //新的点进队列
}
Para profundizar en la comprensión del código anterior, considere un caso especial: todos los puntos que ingresan a la cola se ajustan a la característica de "casco convexo inferior", y la pendiente de la línea recta formada por estos puntos es mayor que todas las pendientes \ (k_i \) , entonces el resultado es: Será expulsado, los puntos que ingresan al equipo no serán expulsados, y el jefe del equipo será reutilizado \ (n \) veces.
![](https://img-blog.csdnimg.cn/20200416163909773.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkxNDU5Mw==,size_16,color_FFFFFF,t_70)
4. Ejemplos
Un ejemplo típico se da a continuación con una pregunta de muestra.
HDU 3507 Imprimir artículo http://acm.hdu.edu.cn/showproblem.php?pid=3507
Descripción del tema : Imprima un artículo que contenga N palabras, el costo de impresión de la i-ésima palabra es Ci. El costo de imprimir k palabras en una línea es que M es una constante. ¿Cómo organizar los artículos para minimizar los costos?
Entrada : hay muchos casos de prueba. Para cada caso de prueba, hay dos números N y M en la primera fila (0≤n≤500000, 0≤M≤1000). Luego, hay N números en las siguientes 2 filas de N + 1. La entrada se termina con EOF.
Salida : un número que indica el costo mínimo de impresión del artículo.
Entrada de muestra :
5 5
5
9
5
7
5
Salida de muestra :
230
El significado del título es: hay números \ (N \) y una constante \ (M \) , el número \ (N \) se divide en varias partes, y el valor calculado de cada parte es el cuadrado de la suma de estas partes más \ (M \) , el valor calculado total es la suma de los valores calculados de todas las partes, y encuentra el valor calculado total más pequeño. Como \ (N \) es grande, el algoritmo de \ (O (N ^ 2) \) agota el tiempo de espera.
Supongamos que \ (dp [i] \) denota el costo mínimo de \ (i \) palabras antes de la salida , ecuación de transferencia DP:
\ (dp [i] = min \ {dp [j] + (sum [i] -sum [ j]) 2 + M \} \) \ (0 <j <i \)
donde \ (sum [i] \) representa la suma de los primeros \ (i \) dígitos.
Lo siguiente reescribe la ecuación DP como \ (y = kx + b \) . Primero expanda la ecuación:
\ (dp [i] = dp [j] + sum [i] * sum [i] + sum [j] * sum [j] -2 * sum [i] * sum [j] + M \ )
Mover elementos:
\ (dp [j] + sum [j] * sum [j] = 2 * sum [i] * sum [j] + dp [i] -sum [i] * sum [i] -M \)
Control\ (y = kx + b \) , hay:
\ (y = dp [j] + sum [j] * sum [j] \) , \ (y \) solo está relacionado con \ (j \) .
\ (x = 2 * sum [j] \) , \ (x \) solo está relacionado con \ (j \) , y aumenta con el aumento de \ (j \) .
\ (k = sum [i] \) , \ (k \) solo está relacionado con \ (j \) , y aumenta a medida que \ (i \) aumenta.
\ (b = dp [i] -sum [i] * sum [i] -M \) , \ (b \) solo está relacionado con i, y contiene \ (dp [i] \) .
El código se da a continuación.
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 500010;
int dp[MAXN];
int q[MAXN]; //单调队列
int sum[MAXN];
int X(int x){ return 2*sum[x]; }
int Y(int x){ return dp[x]+sum[x]*sum[x]; }
//double slope(int a,int b){return (Y(a)-Y(b))/(X(a)-X(b));} //除法不好,改成下面的乘法
int slope_up (int a,int b) { return Y(a)-Y(b);} //斜率的分子部分
int slope_down(int a,int b) { return X(a)-X(b);} //斜率的分母部分
int main(){
int n,m;
while(~scanf("%d%d",&n,&m)){
for(int i=1;i<=n;i++) scanf("%d",&sum[i]);
sum[0] = dp[0] = 0;
for(int i=1;i<=n;i++) sum[i]+=sum[i-1];
int head=1,tail=1; //队头队尾
q[tail]=0;
for(int i=1;i<=n;i++){
while(head<tail &&
slope_up(q[head+1],q[head])<=sum[i]*slope_down(q[head+1],q[head]))
head++; //斜率小于k,从队头弹走
int j = q[head]; //队头是最优点
dp[i] = dp[j]+m+(sum[i]-sum[j])*(sum[i]-sum[j]); //计算dp[i]
while(head<tail &&
slope_up(i,q[tail])*slope_down(q[tail],q[tail-1])
<= slope_up(q[tail],q[tail-1])*slope_down(i,q[tail]))
tail--; //弹走队尾不合格的点
q[++tail] = i; //新的点进队尾
}
printf("%d\n",dp[n]);
}
return 0;
}
5. Ejercicios
(1) Embalaje de juguetes Luogu P3195 https://www.luogu.com.cn/problem/P3195
Ecuación DP: \ (dp [i] = min \ {dp [j] + (sum [i] + i− sum [j] −j − L − 1) ^ 2 \} \)
(2) Luogu 4072 SDOI2016 viaje https://www.luogu.com.cn/problem/P4072
Optimización de pendiente bidimensional, ecuación DP: \ ( dp [i] [p] = min \ {dp [j] [p − 1] + (s [i] −s [j]) ^ 2 \} \)