You are on page 1of 24

Solues da maratona de programao da SBC 2013

Autor: Gabriel Dalalio



Introduo
Neste texto sero apresentadas dicas para as solues dos problemas da
prova da sub-regional brasileira da maratona de programao de 2013.
Alm da discusso dos algoritmos esperados, o texto contm dicas de
implementao em C++, links teis para estudo e referncias a problemas
de outras competies que so relacionados aos temas das questes da
prova.
O enunciado das questes da prova pode ser obtido no seguinte link:
http://maratona.ime.usp.br/prim-fase2013/maratona.pdf

Problema A
Zerinho ou Um
Soluo
Esse era o problema mais fcil da prova. Ele pode ser resolvido com a
implementao de uma lgica simples.
A seguir, um exemplo de uma implementao curta para o problema:


Links para estudo
http://www.cplusplus.com/doc/tutorial/control/

Problemas relacionados
http://br.spoj.com/problems/PAR/

Problema B
Balo
Soluo
O problema nos d N segmentos e tambm C pontos de partida para
bales que iram subir e deslizar nos segmentos. Para cada balo temos de
dizer qual o seu destino.
Os valores de N e C podem ir at
5
10 , ento j sabemos que teremos de
ser bem eficientes para descobrir as respostas das consultas, o programa
no passar no tempo se tentarmos simular o caminho do balo para cada
consulta.
Para a resoluo do problema crucial descobrir para cada consulta qual
o primeiro segmento que o balo encosta (ou se ele escapa direto).
Tambm importante saber para cada segmento o que acontece com o
balo depois de tocar esse segmento, para qual segmento o balo vai (ou
se ele escapa). Nessa hora vamos precisar de um algoritmo baseado em
line sweep.
Imagine uma reta vertical passando no plano do problema, vindo desde o
menor valor de x e indo at o maior valor. Acontecem 3 tipos de eventos
interessantes:
- A reta vertical encosta em um segmento.
- A reta vertical desencosta de um segmento.
- A reta vertical chega em um valor de x de onde um balo solto.
Se conseguirmos manter o conjunto de segmentos que toca essa reta em
um determinado instante durante esse processo, isso pode nos ajudar a
descobrir o destino do balo aps ser solto e aps tocar em um segmento.
Alm de simplesmente manter esse conjunto, ser interessante manter
esse conjunto de segmentos ordenado, do segmento que est mais baixo
at o segmento mais alto.
A implementao disso no to simples, por isso esse texto tentar
mostrar alguns detalhes de como fazer isso utilizando C++.
Iremos precisar de estruturas para ponto e segmento. A estrutura de
ponto deve possuir dois inteiros, x e y . A estrutura de segmento deve
possuir dois pontos, 1 p e 2 p tal que 1. 2. p x p x < .
Para manter o conjunto de segmentos ordenados de baixo para cima,
usaremos o set da STL do C++. Iremos criar nossa prpria funo de
ordenao para o set receber os identificadores dos segmentos e orden-
los para que os segmentos fiquem de baixo para cima no conjunto. Veja
uma possvel implementao desse set:

Com essa estrutura de set, pode-se ento jogar os identificadores dos
segmentos no conjunto e eles ficaro ordenados de acordo com a altura
dos segmentos. Agora vamos ver como lidar com os trs tipos de evento:
- 1) Reta vertical encosta em um segmento de nmero k :
o Insira k no conjunto.
o Se [ ]. 1. [ ]. 2. v k p y v k p y > , aps um balo tocar o segmento k , ele
ir escapar para o segmento logo acima de k no conjunto.
- 2) Reta vertical desencosta de um segmento de nmero k :
o Se [ ]. 2. [ ]. 1. v k p y v k p y > , aps um balo tocar o segmento k , ele
ir escapar para o segmento logo acima de k no conjunto.
o Remover k do conjunto.
- 3) Reta vertical chega ao x de uma consulta:
o Se o conjunto de segmentos estiver vazio, o balo escapa
direto nessa consulta.
o Caso contrrio, o primeiro segmento que o balo encostar
o primeiro segmento presente no conjunto, * . () lines begin .
Note que para isso funcionar, se houver empate de coordenada x entre
os eventos, o evento tipo 1 deve preceder o evento tipo 3, e o evento tipo
3 deve preceder o tipo 2. O enunciado deixa explcito que no h x em
comum entre dois segmentos, ento no h empate entre eventos do tipo
1 e 2. Se houvesse x em comum seria necessria uma travessia um pouco
mais complicada nos eventos.
Outro detalhe como pegar um segmento que est no conjunto e
descobrir o segmento acima dele. Isso pode ser feito da seguinte maneira:

Agora que temos as informaes sobre os destinos dos bales aps
baterem nos segmentos e serem soltos nas consultas, podemos utilizar
uma programao dinmica que dado um segmento inclinado, responde
qual seria o destino final do balo. A parte mais difcil j foi, o leitor fica
encarregado dos detalhes de implementao dessa parte final da
resoluo.

Links para estudo
http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=lineSweep

Problemas relacionados
http://www.spoj.com/problems/RAIN1/
Problema C
Chefe
Soluo
dado um grafo direcionado acclico e pede-se um programa que realize
instrues de trocas de vrtices e perguntas sobre antecessores diretos ou
indiretos de um vrtice.
Vamos discutir primeiro a instruo de troca de vrtices. Quando se
realizado uma troca de vrtices no grafo, a estrutura do grafo no muda.
Durante a execuo do programa, s precisamos saber onde est cada
pessoa no grafo e qual a idade de cada vrtice. Podemos manter dois
vetores, [ ] pos i igual ao vrtice atual da pessoa i e [ ] idade i sendo a idade
da pessoa no vrtice i . Dessa maneira, a troca de duas pessoas a e b
pode ser feito em tempo (1) O como mostrado abaixo:

Inicialmente, pode-se fazer [ ] pos i i = para todo i .
Agora sero discutidas duas solues para a outra instruo.
Soluo 1
Para implementar a instruo de pergunta, pode-se realizar uma busca
para cada instruo. Isso pode ser feito usando busca em largura (BFS) ou
busca em profundidade (DFS) atravessando as arestas no sentido
contrrio. A complexidade para cada query ser de ( ) O N M + .
Soluo 2
Como j foi dito, as operaes de troca de vrtices no alteram a estrutura
do grafo. Com isso, podemos pr-calcular todos os antecessores dos
vrtices do grafo.
A relao de antecessor transitiva, se A antecessor de B e B
antecessor de C, ento A antecessor de C. Para achar todos os
antecessores de um vrtice, precisamos calcular o fecho transitivo da
relao (ver transitive closure nos links para estudo). Para calcular o fecho
transitivo pode-se utilizar o algoritmo de Floyd-Warshall.
Inicialmente, caso exista aresta de A para B , faz-se [ ][ ] 1 antecessor A B = ,
caso contrrio [ ][ ] 0 antecessor A B = . Aps isso, utiliza-se o Floyd-Warshall
como mostrado abaixo:

Aps executar esse trecho de cdigo, [ ][ ] antecessor A B igual a 1 se e
somente se A antecessor de B . Possuindo essa tabela, cada instruo
de pergunta pode ser feita em ( ) O N iterando-se por todos os vrtices e
verificando quais deles so antecessores do vrtice perguntado.

Links para estudo
http://en.wikipedia.org/wiki/Depth-first_search
http://en.wikipedia.org/wiki/Breadth-first_search
http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=graphsDataStrucs1
http://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm
http://en.wikipedia.org/wiki/Transitive_closure

Problemas relacionados
http://br.spoj.com/problems/DEPENDEN/
Problema D
Mquina dobradora
Soluo
dado uma fita de N nmeros em um estado inicial. O problema nos
pede para dizer se aps algumas dobras possvel se chegar em um
estado final.
Deve-se atentar ao fato que o limite para N igual a 15. Esse valor baixo
nos sugere que esse problema pode ser resolvido com fora bruta,
testando todas as formas de dobrar recursivamente. possvel se calcular
com uma funo recursiva que o nmero de maneiras de dobrar uma fita
de tamanho 15 na ordem de um milho.
Dependendo da eficincia do programa para calcular todas essas dobras,
talvez seja necessrio algum corte na fora bruta, como por exemplo:
- Se a soma dos nmeros da fita inicial for diferente da soma da fita
final, a resposta N.
- No adianta dobrar mais vezes a fita se ela j est menor que o
estado final.
- Aps uma dobra, os nmeros s podem aumentar. Se o elemento
mximo da fita passar do elemento mximo do estado final,
tambm se pode realizar um corte na busca.

Links para estudo
http://en.wikipedia.org/wiki/Brute-force_search

Problemas relacionados
http://www.urionlinejudge.com.br/judge/problems/view/1395
Problema E
Mergulho
Soluo
O problema pedia para achar quais nmeros inteiros de 1 a N no
apareciam na entrada. O problema poderia ser resolvido utilizando um
vetor com N posies para marcar os nmeros que apareceram na
entrada. Se o nmero i aparece na entrada, pode-se fazer [ ] 1 v i = . Dessa
maneira possvel obter uma soluo com complexidade de tempo ( ) O N .
possvel tambm utilizar a estrutura do set da STL do C++ para obter
uma soluo na complexidade ( .log ) O N N , pois o set possui funes de
insero e busca em tempo (log ) O N .

Links para estudo
http://www.cplusplus.com/doc/tutorial/arrays/
http://www.cplusplus.com/reference/set/set/

Problemas relacionados
Pea perdida OBI 2007 Programao nvel 1 fase 1
http://olimpiada.ic.unicamp.br/passadas/OBI2007/res_fase1_prog/programacao_n1/tarefas_s
olucoes

Problema F
Tringulos
Soluo 1
So dados
5
10 N s pontos em uma circunferncia e pede-se quantos
tringulos equilteros possvel formar com esses pontos. Primeiro fato
que temos de notar o limite para o N . No podemos, por exemplo,
testar todos os trios de pontos ou todos os pares de pontos, nesse caso o
programa exceder o tempo limite.
Uma informao valiosa nesse problema que o tringulo equiltero
determina 3 arcos iguais no crculo, ou seja, podemos tomar a soma de
todos os arcos e dividir por 3 para achar o tamanho do arco que os pontos
de tringulo equiltero determinam. Se a soma no for divisvel por 3, a
resposta para o problema nula.
Seja o vetor [] soma definido por
1
[ ]
i
i
k
soma i X
=
=

, ou seja, o vetor soma


representa a soma acumulada dos arcos da entrada. Para encontrar um
tringulo equiltero, devemos encontrar os trios i , j , k tais que:
[ ] [ ] ( [ ] / 3)
[ ] [ ] 2*( [ ] / 3)
soma j soma i soma N
soma k soma i soma N
= +
= +

Podemos testar todos os valores possveis i , so N possibilidades. Para
achar j e k podemos utilizar busca binria ou a estrutura de set da STL,
resultando em uma soluo com complexidade de tempo ( .log ) O N N .
Soluo 2
Podemos melhorar um pouco o ltimo passo da soluo 1 e conseguir um
algoritmo que roda em tempo linear. A medida que tentamos as
possibilidades para o i em ordem crescente, possvel fazer a busca por j
e k apenas incrementando seus valores.

Veja a seguir um trecho de um cdigo que mostra como implementar isso:

Perceba que apesar dos loops aninhados, esse algoritmo linear no
tempo, pois as variveis i , j , k so incrementadas no mximo N vezes.

Links para estudo
http://www.cplusplus.com/reference/set/set/
http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=binarySearch

Problemas relacionados
http://br.spoj.com/problems/POLIGONO/
Problema G
Linhas de contineres
Soluo 1
dada uma matriz LxC com elementos embaralhados e pede-se o
nmero mnimo de movimentos de troca de linhas ou colunas para
desembaralhar a matriz.
A primeira soluo que ser apresentada construtiva, descreve como
realizar vrios movimentos necessrios at que se descubra que a matriz
est desembaralhada ou que a tarefa impossvel.
Primeira coisa que podemos achar o elemento igual a 1 e trocar sua
coluna com a primeira coluna e sua linha pela primeira linha. A partir
disso, no ser mais possvel mexer na primeira linha e nem na primeira
coluna. A primeira linha deve conter os nmeros de 1 a C e a primeira
coluna deve conter todos os nmeros que possuem resto 1 por C . Caso
contrrio, no haver soluo para o teste.
Agora vamos arrumar a primeira linha e a primeira coluna. Enquanto
houver elementos em posies erradas na primeira linha, troque duas
colunas para colocar pelo menos um elemento a mais em sua posio
correta. Faa o mesmo com a primeira coluna, enquanto houver
elementos errados troque linhas.
Aps isso, se a matriz toda ficar na posio correta, imprima o nmero de
passos que foram feitos. Caso contrrio no h soluo, pois qualquer
troca de coluna ir desarrumar a primeira linha e qualquer troca de linha
ir desarrumar a primeira coluna.
O nmero mximo para a resposta 2 L C + e cada operao de troca de
fileira pode ser feita em tempo linear no nmero de elementos movidos.
Dessa maneira, essa soluo possui complexidade ( ) O LC .

Soluo 2
A segunda soluo que ser apresentada no faz nenhuma alterao na
matriz para descobrir a resposta. Analisaremos as permutaes presentes
na matriz e a soluo ser encontrada a partir da contagem de ciclos de
permutao (ver links para estudo).
Se dois elementos esto na mesma coluna, eles sempre ficaro na mesma
coluna aps as operaes de troca de fileiras. O mesmo acontece com dois
elementos que esto na mesma linhaa, eles sempre estaro na mesma
linha. Se verificarmos que todos os elementos [ ][ ] X i j pertencem mesma
linha do elemento [ ][ 1] X i j e mesma coluna do elemento [ 1][ ] X i j ,
ento h resposta para o problema.
O valor da resposta ser dado pela soma L C + menos o nmero de ciclos
de permutao de uma linha e de uma coluna (todas as linhas equivalem
mesma permutao, o mesmo ocorre para as colunas). A soluo tambm
pode ser implementada na mesma complexidade da soluo 1.

Links para estudo
http://mathworld.wolfram.com/PermutationCycle.html

Problemas relacionados
http://br.spoj.com/problems/CADEIR09/
Problema H
nibus
Soluo
O problema pede a contagem de maneiras que se pode formar uma fila
com dois tipos de nibus. Existem K nibus de tamanho 5 e L nibus de
tamanho 10. Como N mltiplo de 5, podemos comear o problema
dividindo N por 5 e considerar que os nibus tem tamanho 1 e 2.
O prximo passo da soluo obter uma formula recursiva para o
problema. Seja ( ) f n o nmero de maneiras de formar uma fila de
tamanho n . Tem-se (0) 1 f = (uma maneira de formar uma fila vazia) e
(1) f K = (s d para usar um dos K nibus de tamanho 1). Para 2 n > ,
pode-se comear a fila por um nibus de tamanho 1 ou 2. Se escolhermos
o comeo pelo nibus de tamanho 1, h K possibilidades para escolher o
tipo do nibus e h ( 1) f n possibilidades para escolher o resto da fila,
resultando em . ( 1) K f n maneiras. De modo similar, para o comeo com
nibus de tamanho 2 haver . ( 2) L f n maneiras. Com isso,
( ) . ( 1) . ( 2) f n K f n L f n = + para 2 n > .
Descoberta a recorrncia, poderamos utilizar programao dinmica para
obter uma soluo na complexidade de tempo ( ) O N . Porm o valor de N
pode ser muito alto, isso no seria suficiente para passar no tempo.
A recorrncia encontrada linear, assim como a sequncia de Fibonacci.
Vamos aprender agora como calcular ( ) f n rapidamente utilizando
exponenciao de matrizes. Repare que a seguinte identidade vlida:
| | | |
0
( 2) ( 1) . ( 1) ( )
1
L
f n f n f n f n
K
(
=
(


Com essa frmula, pode-se chegar no valor de ( ) f n a partir dos valores de
(0) f e (1) f da seguinte maneira:
| | | |
| | | | | |
| | | |
2
0
(0) (1) . (1) (2)
1
0 0
(0) (1) . (1) (2) . (2) (3)
1 1
0
(0) (1) . ( ) ( 1)
1
n
L
f f f f
K
L L
f f f f f f
K K
L
f f f n f n
K
(
=
(

( (
= =
( (

(
= +
(


Para obter a resposta do problema, basta agora que tenhamos um
mtodo rpido para fazer exponenciao de matrizes. Existe um mtodo
rpido para isso, veja o link para estudo sobre exponentiation by squaring.
Segue abaixo um pseudo cdigo que mostra como fazer a exponenciao
com (log ) O N multiplicaes.

No se esqueam de sempre tirar resto por
6
10 nas operaes!

Links para estudo
http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=dynProg
http://en.wikipedia.org/wiki/Exponentiation_by_squaring
Problemas relacionados
http://www.urionlinejudge.com.br/judge/problems/view/1422
https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&category=3
28&page=show_problem&problem=2304
Problema I
Remendo
Soluo
So dadas N posies de furos em um pneu de bicicleta. Com remendos
de tamanho
1
T e
2
T , quer se cobrir todos os furos utilizando o mnimo
total de remendo.
Um fato interessante para a busca da soluo tima que toda soluo
pode ser transformada em um uma soluo onde todos os remendos
cobrem um furo em uma de suas extremidades. Isso ocorre porque
podemos transladar os remendos at que isso acontea e sem descobrir
nenhum furo.
Uma das complicaes do problema o fato do pneu ser circular. Vamos
primeiro esquecer esse fato e fingir que o pneu uma tira esticada.
Imagine o primeiro furo
1
F dessa tira. Esse furo dever ser coberto por um
remendo de tamanho
1
T ou
2
T . Alm disso, podemos colocar os remendos
com uma das pontas exatamente em
1
F . Aps colocar um remendo,
podemos esquecer todos os pontos que ficaram cobertos. Dessa maneira,
a soluo recai sobre o mesmo problema inicial, mas com um tira com
menos furos. Quando isso acontece, pode-se tentar solucionar o problema
com programao dinmica.
Seja a funo ( ) cobertura i que retorna qual o mnimo de remendo para se
cobrir os furos de 1 a i . Pode-se obter o seguinte pseudocdigo para essa
funo:

Esse pseudocdigo no mostra o processo de memoizao, mas ele deve
ser feito, necessrio salvar as respostas de ( ) cobertura i para que a funo
no calcule a mesma coisa vrias vezes.
Com isso resolvemos o problema para uma tira, no para o pneu. Mas o
pneu tem N possibilidades para ser transformado em uma tira, pode-se
cortar o pneu entre os N furos. Deve-se testar todas as tiras e pegar a
menor resposta. Como a funo cobertura tem complexidade ( ) O N , pode-
se obter a resposta para todas as tira em
2
( ) O N . Outro detalhe que os
valores de j que a funo cobertura usa podem ser pr-calculados em
2
( ) O N , ( .log ) O N N ou ( ) O N utilizando loops ou usando tcnicas que foram
usadas nas solues do problema F Tringulos.

Links para estudo
http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=dynProg
Problemas relacionados
https://icpcarchive.ecs.baylor.edu/index.php?option=onlinejudge&page=show_problem&prob
lem=1152
Problema J
Caminho
Soluo 1
O problema nos d um grafo de N vrtices e M arestas. Nesse grafo
necessrio obter a resposta de S consultas. Escolhidas duas cidades,
deve-se imprimir o valor mximo que pode ter a menor aresta de um
caminho entre as duas cidades.
possvel modificar a funo de distncia dos algoritmos de Dijkstra ou
Floyd-Warshall para eles obterem as respostas das consultas do problema.
Utilizando o algoritmo de Dijkstra, seria necessrio execut-lo para cada
consulta, obtendo soluo na complexidade ( .( ).log ) O S N M N +
implementando o algoritmo com fila de prioridade. Usando o Floyd-
Warshall, possvel pr-processar todas as consultas, a complexidade
seria
3
( ) O N S + . Nenhuma dessas tentativas passa no tempo.
Uma alternativa seria tentar resolver o problema por uma busca linear na
resposta. Para cada consulta entre as cidades A e B , comeasse um grafo
sem arestas. A cada passo, adiciona-se ao grafo a maior aresta que ainda
no foi colocada. Quando existir caminho entre A e B , a resposta para a
consulta dada pelo valor da ltima aresta adicionada. A existncia do
caminho poderia ser verificada utilizando algoritmo de union-find. Isso
possibilita a resoluo em complexidade ( . ) O S M , que ainda no
suficiente para passar.
Analisando melhor essa ltima resoluo, no precisamos adicionar as
arestas ao grafo que no liguem vrtices em componentes conexas
diferentes. Dessa maneira, o algoritmo executado seria idntico ao
algoritmo de Kruskal para achar a rvore geradora mxima do grafo. Isso
nos leva a um fato muito importante para a resoluo do problema: as
respostas de todas as consultas permanecem as mesmas se analisarmos
apenas a rvore geradora mxima do grafo. Ou seja, inicialmente
podemos obter essa rvore e pensar agora como responder as consultas
para a rvore.
A rvore geradora mxima normalmente obtida pelo algoritmo de
Kruskal ou pelo algoritmo de Prim. No caso da soluo que ser discutida
agora, o uso do algoritmo de Prim ser mais prtico, pois ele encontra a
rvore geradora j determinando um pai para cada n. Com o Kruskal,
necessrio utilizar uma busca como a DFS para a obteno dos pais.
Falta pensar como proceder as consultas na rvore. A grande vantagem da
rvore que s h um caminho entre um par de vrtices. Para cada
consulta necessrio achar esse caminho e dizer a menor aresta. Isso
pode ser feito com uma busca para cada consulta, o que leva a uma
complexidade de ( . ) O S N , que ainda no passa no tempo.
Para encontrar rapidamente o caminho entre dois vrtices numa rvore
pode-se utilizar a tcnica de encontrar o primeiro ancestral comum entre
dois vrtices em tempo logartmico. Essa tcnica explicada no artigo do
site do Topcoder apresentado na seo de links para estudo. Mesmo
assim, esse texto explicar a implementao da aplicao dessa tcnica
especificamente para encontrar a menor aresta em um caminho na
rvore.
Aps encontrar a rvore geradora mxima, deve-se encontrar a distncia
de cada vrtice at a raiz, ou seja, a altura de cada vrtice na rvore.
Pode-se escolher qualquer vrtice como raiz, sua altura ser igual 0. Deve-
se calcular tambm uma tabela [ ][ ] t i j com dois valores. Seja o caminho
formado por 2
j
arestas que comea no vrtice i e vai em direo raiz. O
vrtice final do caminho ser igual a [ ][ ]. t i j pai e a menor aresta do
caminho ser [ ][ ]. t i j val .
Os valores de [ ][0] t i so calculados facilmente a partir da construo da
rvore. O cdigo a seguir mostra como calcular o resto da tabela com
complexidade de tempo constante para cada elemento.

Com a tabela pronta, possvel utiliz-la nas consultas para obter uma
complexidade logartmica para achar a menor aresta entre dois vrtices.
Sendo logn o maior nmero tal que (1 log ) n n << < , veja o cdigo a seguir
que encontra a menor aresta entre dois vrtices A e B .

Ao final da funo, as variveis a e b sempre acabam iguais ao primeiro
ancestral comum dos dois vrtices passados para a funo, mas nessa
questo a identidade do ancestral no precisou ser utilizada. Ao final do
clculo da arvore geradora mxima, do clculo da tabela e do clculo das
respostas das consultas, obtemos um algoritmo com complexidade de
tempo igual a ( .log .log .log ) O M M N N S N + + , que suficiente para passar
no tempo limite do problema.
Soluo 2
A segunda soluo que ser discutida agora leva em conta o fato de que
no precisamos responder cada consulta no mesmo momento em que
fazemos leitura da mesma na entrada. Por exemplo, pode-se fazer a
leitura de todas as consultas e s aps isso responder todas as consultas
na ordem dada na entrada.
Ser proposta aqui uma maneira de se modificar o algoritmo de union-find
durante a execuo do algoritmo de Kruskal para que ao final da execuo
todas as consultas j estejam respondidas.
Durante a execuo do algoritmo de Kruskal, manteremos para cada
componente conexa um conjunto de identificadores de consultas.
Inicialmente, o grafo comea sem arestas, cada vrtice representa uma
componente diferente e sendo assim cada um possui um conjunto de
identificadores.
Aps a leitura de todas as consultas, se a consulta de nmero i dada
pelos vrtices a e b , adiciona-se o inteiro i aos conjuntos dos vrtices a e
b . dessa maneira ento que iremos comear o nosso algoritmo, vamos
analisar agora como fazer a juno das componentes e responder as
consultas.
O algoritmo de Kruskal ir adicionando ordenadamente as arestas no
grafo, da maior para a menor. As arestas que realmente sero inseridas
juntam duas componentes conexas diferentes. Quando essa juno
ocorrer, iremos juntar tambm o conjunto de identificadores dessas
componentes. Se uma aresta juntar duas componentes que contm um
mesmo identificador de consulta, a resposta dessa consulta ser igual ao
valor da aresta. Com isso em mente, veja a seguir o pseudocdigo da
modificao do algoritmo de union-find para resolver o problema.

Aps usar a funo join para todas as arestas do grafo, indo da maior para
a menor, todas as consultas estaro respondidas no vetor resp . Agora falta
analisar a complexidade do algoritmo.
Os conjuntos de identificadores s precisam permitir as operaes de
insero, travessia por todo o conjunto e deleo de todo o conjunto.
Usando a estrutura de vector da STL ou usando uma lista ligada, a insero
pode ser feita em tempo constante e as outras duas operaes podem ser
feitas em tempo linear no nmero de elementos do conjunto.
Em uma anlise ingnua, poder-se-ia dizer que a complexidade da funo
join de ( ) O S , levando o algoritmo ter complexidade ( . ) O N S , porm
temos de analisar melhor quantas operaes pode-se ter no total.
Toda vez que um identificador muda de conjunto, ele vai para um
conjunto maior ou de mesmo tamanho que o conjunto em que ele estava.
Aps a passagem de todos os elementos do conjunto de j para o
conjunto de i , os elementos que estavam no conjunto de j passam a
estar num conjunto que pelo menos duas vezes maior que o conjunto
onde eles estavam. Isso significa que cada identificador de consulta
movido no mximo
2
log (2 ) S vezes, pois nenhum conjunto pode ter
tamanho maior que 2.S .
Aps essa anlise, descobrimos que o algoritmo pode ser executado em
complexidade de tempo igual a ( .log .log ) O M M S S + , o que inclui a
ordenao das arestas e a execuo do algoritmo de Kruskal com o union-
find modificado.
A vantagem da soluo 2 que sua implementao pode ficar bem menor
que a implementao da soluo 1, podendo ter por volta de 80 linhas de
cdigo em C++. Um detalhe de implementao que pode ser destacado
que durante a execuo da funo join , pode-se descartar identificadores
de consultas j respondidas para diminuir o tempo mdio de execuo.

Links para estudo
http://pt.wikipedia.org/wiki/Algoritmo_de_Prim
http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=lowestCommonAncesto
r#Lowest Common Ancestor (LCA)
http://pt.wikipedia.org/wiki/Algoritmo_de_Kruskal
http://en.wikipedia.org/wiki/Disjoint-set_data_structure
Problemas relacionados
https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show
_problem&problem=2297
http://br.spoj.com/problems/ANTS10/
http://www.spoj.com/problems/KOICOST/

You might also like