O problema é baseado em um problema de concurso leetcode ( https://doocs.github.io/leetcode/en/lc/3281/ ). Pode-se reduzi-lo ao seguinte problema: seja s um array ordenado e d um inteiro positivo, minimize ( s [i]- s [j]+ d )/(ij). Em que i>j.
A maioria das respostas foi baseada em busca binária para limitar uma solução ótima, para a qual a complexidade de tempo é dependente de max( s )-min( s ). Então é possível construir um algoritmo que seja linear/subquadrático em relação ao tamanho de s ?
Você pode fazer isso em tempo O(n log n).
Primeiro, observe que se representássemos graficamente os pontos baixos
s[i]
junto com os pontos altoss[i]+d
, nossa tarefa seria encontrar a linha com a menor inclinação possível de um ponto baixo para um ponto alto à sua direita.À medida que iteramos pelos pontos altos, podemos determinar qual dos pontos baixos à esquerda se conecta com a menor inclinação e, então, lembrar qual é o melhor.
Podemos encontrar o melhor ponto de conexão em tempo O(log n), porque é garantido que ele esteja no casco convexo superior dos pontos baixos à esquerda. Conforme iteramos pelos pontos altos, podemos usar o algoritmo de cadeia monótona para construir simultaneamente o casco convexo superior dos pontos baixos à sua esquerda. Isso leva tempo constante amortizado por ponto.
Dado o casco convexo superior de pontos à esquerda, podemos usar uma busca binária para encontrar o melhor ponto de conexão, porque a inclinação dos segmentos do casco convexo diminui monotonicamente. Para todos os segmentos à esquerda do melhor ponto de conexão, o ponto alto atual estará abaixo ou em sua linha, e para todos os segmentos à direita do melhor ponto de conexão, o ponto alto atual estará acima ou em sua linha.
Aqui está uma implementação em python: