Escrito por Henrique Vianna
Conhecimento Prévio Necessário:
Problema Base
Você é dono de um hotel que receberá hóspedes. O -ésimo deles pretende se hospedar no hotel entre os dias e (). Sabendo que hóspedes chegam de manhã e saem de noite, qual é o número máximo de hóspedes que estarão no hotel em um mesmo momento?
Considere que essa ocupação máxima ocorra no dia . É fácil ver que podemos considerar como "candidatos" para apenas os momentos em que há alguma alteração no número de hóspedes presentes no hotel. Em outras palavras, só precisamos considerar os dias presentes em ou . Com isso, já temos uma solução : basta testar cada tempo candidato e checar quantos hospedes estarão no hotel. Entretanto, essa complexidade não é suficiente para passar o problema. Assim, é intuitivo pensar em alguma maneira de checar todos os tempos candidatos "de uma vez", ou seja, passando por cada intervalo de hospedagem um número fixo de vezes. Para fazer isso, utilizaremos o conceito de line sweep!
Introdução à Line Sweep
Imagine os intervalos de hospedagem como intervalos no plano, junto a uma reta vertical que representa a passagem do tempo. Em um dado momento, o numero de hóspedes que estão no hotel é simplesmente o número de intervalos que intersectam com nossa reta (o tempo). Precisamos, então pensar em como esse número varia. Utilizando a observação do paragrafo anterior, percebe-se que só há dois casos em que a ocupação do hotel de fato muda:
- Um hóspede acaba de chegar: nesse caso, a ocupação aumenta em exatamente .
- Um hóspede sai do hotel: nesse caso, a ocupação diminui em exatamente .
Podemos, portanto, simular o movimento dessa linha considerando apenas esses dois casos. Para isso, processaremos esses eventos de entrada e saída ordenados pelo tempo: um evento pode ser resumido a apenas duas informações: o tempo em que ocorre, e o tipo de evento (1 ou 2). Podemos então criar esses eventos, ordená-los por tempo (desempate pelo tipo de evento, sendo que a entrada sempre ocorre antes da saída) e então varrê-los em ordem, mantendo em variáveis auxiliares a resposta e o número de hóspedes no momento atual.
A ideia da line sweep é justamente essa: processar os eventos de interesse (alterações na resposta, queries, etc.) numa ordem especifica enquanto mantemos informações de relevância. Essas informações formam a nossa line, que é consultada no cálculo da resposta. A line é normalmente representada por uma estrutura de dados, podendo ser tanto algo simples como um inteiro (como no caso acima) quanto algo mais complexo, como um set ou uma Segment Tree. Além disso, por estarmos varrendo os eventos em ordem, estamos fazendo uma sweep (uma "varredura), daí o nome do algoritmo.
Para melhor visualizar o algoritmo, veja o código do problema discutido e a representação gráfica do caso , e
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <bits/stdc++.h> | |
using namespace std; | |
int main() | |
{ | |
int n; cin >> n; | |
vector<pair<int, int>> eventos; | |
for(int i = 1; i <= n; i++) | |
{ | |
int s, e; cin >> s >> e; | |
// Crio os eventos da sweep | |
eventos.emplace_back(s, 1); | |
eventos.emplace_back(e, 2); | |
} | |
sort(eventos.begin(), eventos.end()); | |
int hospedes = 0, resp = 0; | |
// "Sweep" | |
for(auto [tempo, tipo] : eventos) | |
{ | |
if(tipo == 1) hospedes++; | |
else if(tipo == 2) hospedes--; | |
resp = max(resp, hospedes); | |
} | |
cout << resp << '\n'; | |
} |
Implementação
Para facilitar a implementação, é frequente o uso de uma struct/class que organize melhor as informações de um evento. Tal estrutura normalmente contem pelo menos duas componentes básicas: o elemento de comparação (normalmente tempo ou coordenadas no plano) e o tipo de evento. Deve-se implementar um comparador (ou uma função de comparação) que possibilite a ordenação dos elementos. Além disso, vale destacar a importância de se considerar a ordem relativa entre eventos de tipos diferentes mas que possuem o mesmo elemento de comparação (no problema acima, a resposta poderia ser diferente se não processássemos todas as chegadas de hospedes antes das saídas num mesmo dia). Segue um exemplo de implementação dessa estrutura:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct Evento | |
{ | |
int tempo, tipo, id; | |
Evento(int a = 0, int b = 0, int c = 0) : tempo(a), tipo(b), id(c) { } | |
// O elemento id eh util quando temos que responder queries | |
// Pode ser que seja util guardarmos outras informacoes tambem | |
bool operator < (Evento outro) | |
{ | |
if(tempo == outro.tempo) return tipo < outro.tempo; | |
return tempo < outro.tempo; | |
} | |
}; |
Para competidores mais experientes, pode ser mais interessante a utilização da estrutura tuple, com comparador já definido.
Limitações
Problemas que envolvem múltiplas queries podem frequentemente ser resolvidos por meio dessa técnica (assim como no caso dos problemas da lista abaixo!). Isso, porém, só é possível quando podemos respondê-las de forma offline. Em outras palavras, só podemos pensar em usar uma line sweep quando uma query não precisa ser respondida imediatamente depois de ser recebida. Queries precisam ser necessariamente respondidas online quando uma query depende da resposta da anterior.
Pratique!
Para melhor compreender esse algoritmo, a pratica é essencial. Segue uma lista de problemas relacionados a Line Sweep:
- Livros | Neps Academy - TFC 2021 (similar ao discutido na aula)
- Cortando o Papel | Neps Academy - OBI 2017 Segunda Fase (solução)
- Room Allocation
- Intersection Points
- Distinct Values Queries
- Wifi | Neps Academy - OBI 2018 Segunda Fase (solução)
- Area of Rectangles (solução)
- 3SUM - USACO 2020 Gold
- Bolsa de Brinquedos | Neps Academy - Seletiva IOI 2019 (solução)
- Fortune Telling 2 - JOI 2014