[OpenJudge 3866] Ventana deslizante [cola monótona]
Esta es una nota de solución de problema, que parece considerarse un problema clásico. Enlace de pregunta: OpenJudge - 7: Ventana deslizante
Límite de tiempo total: 12000 ms Límite de memoria: 65536 kB
Descripción
Dada una longitud de n (n<= 10 ^6) matriz. Hay una ventana deslizante de tamaño k que se mueve desde el extremo izquierdo al extremo derecho de la matriz. Puedes ver k números en la ventana. La ventana se desliza un número hacia la derecha a la vez.
Aquí tienes un ejemplo:
La matriz es [1 3 -1 -3 5 3 6 7], k = 3.
Tu tarea es obtener los valores máximo y mínimo de la ventana deslizante en cada posición.
Entrada
La entrada consta de dos líneas.
La primera línea incluye n y k, que representan la longitud de la matriz y el tamaño de la ventana respectivamente.
La segunda línea contiene n números.
Salida
La salida consta de dos líneas.
La primera línea contiene el valor mínimo para cada posición en la que la ventana se mueve de izquierda a derecha.
La segunda línea contiene el valor máximo para cada posición en la que la ventana se mueve de izquierda a derecha.
Entrada de muestra
Salida de muestra
Después de que comencé a entrar en contacto con STL, descubrí que set y map son realmente poderosos y fáciles de usar. Para resolver este problema, utilicé directamente multiset para simular el deslizamiento.
Dado que la capa inferior de set/multiset se implementa usando árboles rojo-negro, aunque es violento, aún puede pasar 900 ms en OpenJudge. Sin embargo, un caso enviado a Luogu informó un TLE durante más de 1 segundo, ¡así que estudiemos la estructura de datos "correcta" para esta pregunta!
El método de fuerza bruta utiliza set. Cada vez que se inserta un nuevo elemento, se debe eliminar un elemento antiguo al mismo tiempo. La posición del elemento antiguo en el conjunto debe conocerse mediante otra búsqueda. En este proceso, están involucrados los pasos de búsqueda y eliminación. Reutilice la información de tamaño para otros elementos dentro de la ventana determinada por la marca de tiempo anterior. No queremos reutilizar esta información, después de todo, en todo el proceso, solo nos enfocamos en el valor máximo dentro de la ventana.
Se debe utilizar una estructura de datos más concisa para almacenar estos valores y debe guardar los dos aspectos siguientes de la información: (1) El orden en que aparecen los elementos en el conjunto de datos, que es más adecuado. para la situación aquí. Es una cola; (2) Una estructura de tamaño jerárquico dentro del conjunto de datos, o una estructura monótona, que puede garantizar que cada consulta máxima/mínima se pueda devolver rápidamente. Estos dos datos corresponden a los dos comportamientos de la ventana, uno es la entrada y salida de elementos y el otro es la consulta del mejor valor.
Con estos requisitos previos, podemos comenzar a diseñar la estructura de datos (cola monótona) utilizada en esta pregunta, que satisface nuestras necesidades de manera exquisita. En este problema se utiliza un deque.
Durante el proceso de establecimiento de la ventana (desde leer el primer número hasta leer el número de tamaño (tamaño de la ventana)) y deslizar, mantenemos una cola cuyo comportamiento es consistente con una cola normal, pero lo hará. elimina automáticamente los elementos que es poco probable que sean el resultado de una consulta de mínimo/máximo. Tomando el valor mínimo como ejemplo, si un número aparece detrás del número pero es menor que y, luego de insertarlos, estos dos números nunca tendrán la oportunidad de aparecer como el valor mínimo de la ventana.
Para lograr esto, cada vez que entra un nuevo elemento en la parte posterior, comparamos su tamaño con el tamaño del elemento anterior que entró (la parte posterior actual). Si el nuevo elemento es más pequeño, el reverso actual se elimina, aparece y permite que el siguiente reverso se compare con el nuevo elemento si el nuevo elemento es más grande o llega hasta el principio, ya que este elemento puede convertirse en el valor mínimo; en el futuro, se almacenará primero al final de la cola.
Solo necesitamos implementar la función emergente del elemento en el lado izquierdo de la ventana.
En una buena situación, deje que aparezca el primer elemento existente (el frente); sin embargo, es posible que el elemento en el lado izquierdo de la ventana haya sido eliminado y el frente puede no estarlo; No importa, lo cambiamos al subíndice del elemento de almacenamiento, para que podamos monitorear si debe aparecer el frente de.
A lo largo del algoritmo, las comparaciones que hacemos casi siempre son necesarias. Cada vez que se ingresa un nuevo elemento, como máximo se requieren comparaciones de tamaño. Sin embargo, la frecuencia de tantas comparaciones será muy baja (si se ingresa un nuevo elemento y se requieren comparaciones de tamaño, significa que la ventana se incrementa por completo y la ventana se incrementa por completo). El nuevo elemento es el más pequeño y agregar lo siguiente Un elemento destruirá este incremento o costará una pequeña cantidad de comparaciones (es decir, no siempre se comparará el tamaño por veces), por lo que el rendimiento de este algoritmo debería ser muy bueno. (Edición del 6/7/2022: De hecho, se puede ver directamente que la complejidad del tiempo amortizado es .)
Esta también es la situación real. Después de usar la cola monótona (implementada con std::list), pasa en 300 ms en OpenJudge: