You are on page 1of 62

Minicurso em

Eficincia de Algoritmos
Guloso
verso 1.6

Prof. D.Sc. Fabiano Oliveira


fabiano.oliveira@ime.uerj.br

Guloso

uma tcnica de construo de algoritmos


que possui as seguintes caractersticas:
usualmente, resolve um problema de otimizao
a soluo do problema pode ser descrita recursivamente
(subestrutura tima)
diferentemente da Programao Dinmica, nem todas as
formas de gerar subproblemas so consideradas; dentre
elas, escolhe-se a que melhor no momento ("escolha
gulosa")
a escolha gulosa s depende das escolhas feitas nas
etapas anteriores (nunca daquelas que esto por vir)

Guloso

Em geral:
uma maneira natural e intuitiva de se resolver
problemas
conduz a algoritmos eficientes
nem sempre conduz ao resultado timo. Portanto,
necessrio justificar (provar) a correo

Guloso

A resoluo de um problema via Guloso


(Greedy) normalmente envolve 5 passos:
1. Caracterizar a substrutura tima do problema
2. Definir o valor de uma soluo tima de cada
subproblema recursivamente
3. Caracterizar a escolha gulosa (para cada subproblema,
mostrar que uma soluo tima de um dos
subsubproblemas pertence soluo tima do
subproblema)
4. Computar uma soluo tima de cada subproblema de
forma "top-down"
5. Transformar o algoritmo recursivo em iterativo

Troco Mnimo

Guloso
Problema: Dado o conjunto de moedas M =
{1, 5, 10, 25, 50, 100}, determinar o nmero
mnimo de moedas para dar um troco de
valor N
Exemplo: N = 37 mnimo de 4 moedas

Guloso
Passo 1: Caracterizar a subestrutura tima
do problema
Seja TM(N) o troco mnimo de N.
(Ex: TM(37) = 4 para M = {1, 5, 10, 25, 50, 100})
Assuma N > 0. Suponha que m M pertence a um troco mnimo
T de N. O que dizer de |T - {m}|?

Guloso
Passo 1: Caracterizar a subestrutura tima
do problema
Afirmamos que |T - {m}| = TM(N - m), pois caso contrrio:

Se |T - {m}| > TM(N - m), ento poderamos substituir todas as


moedas de T exceto por m por um troco mnimo de N - m. Assim,
obtendo um troco para N com menos moedas que T, contradizendo
que T troco mnimo
Se |T - {m}| < TM(N - m), ento as moedas de T desconsiderando-se
m formam um troco de N - m com menos moedas que um troco
mnimo de N - m, uma contradio

Guloso
Passo 2: Definir o valor de uma soluo
tima de cada subproblema recursivamente
Seja m M.
Se soubssemos que m pertence a um troco mnimo de N, ento
TM(N) = 1 + TM(N-m). Mas no sabemos. O que fazer?
Testar todas as possveis escolhas!

Guloso
Passo 2: Definir o valor de uma soluo
tima de cada subproblema recursivamente
Recurso:
TM(N) = 0, se N = 0
TM(N) = min { 1 + TM(N-m) | m M, m N }, se N > 0
Alguma diviso particularmente interessante?

Guloso
Passo 3: Caracterizar a escolha gulosa
Usar a moeda de maior valor! Funciona, pois...

Guloso
Prova de Correo da Escolha Gulosa:
Em um troco mnimo, a quantidade mxima de moedas de cada tipo
a seguinte:
Moeda

10

25

50

100

# Mximo

Se num troco mnimo houvessem 5 moedas de 1, por exemplo,


poderamos substitu-las por uma moeda de 5 obtendo um troco
menor que o mnimo, uma contradio

Analogamente se justifica cada quantidade mxima da tabela

Guloso e Programao
Dinmica
Prova de Correo da Escolha Gulosa:
Em um troco mnimo, a quantidade mxima de moedas de cada tipo
a seguinte:
Moeda

10

25

50

100

# Mximo

Se N < 5, somente moedas de 1 so usadas


Se 5 N < 10, o mximo valor obtenvel sem usar uma moeda de 5
4. Portanto, a moeda de 5 necessria

Guloso e Programao
Dinmica
Prova de Correo da Escolha Gulosa:
Em um troco mnimo, a quantidade mxima de moedas de cada tipo
a seguinte:
Moeda

10

25

50

100

# Mximo

Se 10 N < 25, o mximo valor obtenvel sem usar uma moeda de 10


9. Portanto, uma moeda de 10 necessria

Guloso e Programao
Dinmica
Prova de Correo da Escolha Gulosa:
Em um troco mnimo, a quantidade mxima de moedas de cada tipo
a seguinte:
Moeda

10

25

50

100

# Mximo

Se 25 N < 50, o mximo valor obtenvel sem usar uma moeda de 25


24 (pois 2 moedas de 10 e uma de 5 pode ser trocada por uma de
25). Portanto, uma moeda de 25 necessria

Guloso e Programao
Dinmica
Prova de Correo da Escolha Gulosa:
Em um troco mnimo, a quantidade mxima de moedas de cada tipo
a seguinte:
Moeda

10

25

50

100

# Mximo

Se 50 N < 100, o mximo valor obtenvel sem usar uma moeda de


50 49. Portanto, uma moeda de 50 necessria

Guloso e Programao
Dinmica
Prova de Correo da Escolha Gulosa:
Em um troco mnimo, a quantidade mxima de moedas de cada tipo
a seguinte:
Moeda

10

25

50

100

# Mximo

Se 100 N, o mximo valor obtenvel sem usar uma moeda de 100


99. Portanto, uma moeda de 100 necessria

Guloso e Programao
Dinmica
Prova de Correo da Escolha Gulosa:
Em um troco mnimo, a quantidade mxima de moedas de cada tipo
a seguinte:
Moeda

10

25

50

100

# Mximo

Portanto, a cada passo, TrocoMinimo no escolhe


uma moeda que possa estar fora de uma soluo
tima. Isto implica que a soluo final tima

Guloso
Passo 4: Computar uma soluo tima de
cada subproblema de forma "top-down"

Guloso
funo TrocoMinimo(val Troco: Inteiro): Famlia
var moeda: Inteiro
incio
se Troco = 0 ento
retornar ( )
seno
moeda mx { m M | m Troco }
retornar ({moeda} U TrocoMinimo(Troco - moeda))
fim-se
fim-funo
... // leitura de N
escrever(TrocoMinimo(N))

Guloso
Passo 5: Transformar o algoritmo recursivo
em iterativo

Guloso
funo TrocoMinimo(val Troco: Inteiro): Famlia
var TM: Famlia //uma lista ligada
moeda: Inteiro
incio
TM
enquanto Troco > 0 faa
moeda mx { m M | m Troco}
TM TM U {moeda}
Troco Troco - moeda
fim-para
retornar (TM)
fim-funo
... // leitura de N e M()
escrever (TrocoMinimo(N))

Guloso
funo TrocoMinimo(val Troco: Inteiro): Famlia
var TM: Famlia //uma lista ligada
moeda: Inteiro
incio
TM
enquanto Troco > 0 faa
moeda mx { m M | m Troco}
TM TM U {moeda}
Troco Troco - moeda
Complexidade de Tempo:
fim-para
O(N.|M|)
retornar (TM)
fim-funo
analisando melhor...
O(TM(N).|M|)
... // leitura de N e M()
escrever (TrocoMinimo(N))

Guloso
funo TrocoMinimo(val Troco: Inteiro): Famlia
var TM: Famlia //uma lista ligada
moeda, mm: Inteiro
incio
TM, mm , |M|
enquanto Troco > 0 faa
enquanto Troco < M(mm) faa mm mm - 1 fim-enquanto
moeda M(mm)
TM TM U {moeda}
Troco Troco - moeda
fim-para
retornar (TM)
fim-funo
... // leitura de N e M() - assumir M ordenado
escrever (TrocoMinimo(N))

Guloso
funo TrocoMinimo(val Troco: Inteiro): Famlia
var TM: Famlia //uma lista ligada
moeda, mm: Inteiro
incio
TM, mm , |M|
enquanto Troco > 0 faa
enquanto Troco < M(mm) faa mm mm - 1 fim-enquanto
moeda M(mm)
TM TM U {moeda}
Troco Troco - moeda
Complexidade de Tempo:
fim-para
O(TM(N) + |M|)
retornar (TM)
fim-funo
... // leitura de N e M() - assumir M ordenado
escrever (TrocoMinimo(N))

Guloso
Por outro lado, qualquer algoritmo que resolva o
problema de Troco Mnimo deve escrever o troco
mnimo ao final de sua computao e deve, no pior
caso, ler todo o conjunto de moedas disponvel para dar
troco. Portanto, a complexidade de resoluo de
qualquer algoritmo para este problema (TM(N) +
|M|)
Portanto, o algoritmo anterior timo

Seleo Mxima de
Atividades

Guloso

Problema: Dado um conjunto de atividades {A1, ..., AN}, Ai


com horrios de incio si e de fim ei, determinar o maior
nmero de atividades duas a duas sem interseo (Ai e Aj
no tem interseo se ou ei sj, ou ej si)

Exemplo:
s = (1
e = (4

3
5

0
6

5
7

3
8

5
9

6 8 8 2 12)
10 11 12 13 14)

{A3, A7, A11} uma soluo vivel. a maior?

Guloso
Passo 1: Caracterizar a subestrutura tima
do problema
Assuma que o conjunto de atividades esteja ordenado por ordem de trmino, isto ,
i < j => ei ej
Seja Si,j o conjunto de atividades que esto entre Ai e Aj, isto , atividades que
comeam depois de Ai e terminam antes de Aj (permitimos i = 0 e j = N+1, com o
significado de que no h restrio respectivamente esquerda e direita).
Seja S(i, j) o tamanho de uma seleo tima de atividades de Si,j
Seja S uma seleo tima das atividades de Si,j. Suponha que Az S. Sejam A o
subconjunto de S das atividades que antecedem Az e D aquele das atividades que
sucedem Az. O que dizer de A e D?

Guloso
Passo 1: Caracterizar a subestrutura tima
do problema
Afirmamos que |A| = S(i, z) e |D| = S(z, j), pois caso contrrio:

Se |A| < S(i, z), ento podemos substituir todas as atividades em A de


S por aquelas que formam uma seleo tima de S1,z obtendo assim
uma seleo de Si,j maior que |S| = S(i, j), uma contradio
Se |A| > S(i, z), ento A uma seleo de Si,z maior que
S(i, z), uma contradio
Contradies anlogas so obtidas em relao a |D| e S(z, j)

Guloso
Passo 2: Definir o valor de uma soluo
tima de cada subproblema recursivamente
Se soubssemos que Az pertence a uma seleo tima de Si,j
ento saberamos que:
S(i, j) = 1 + S(i, z) + S(z, j)
Mas no sabemos. O que fazer?
Testar todas as possveis escolhas!

Guloso
Passo 2: Definir o valor de uma soluo
tima de cada subproblema recursivamente
Recurso:
S(i, j) = 0, se Si,j =
S(i, j) = mx {1 + S(i, z) + S(z, j) | i z j}, se Si,j
Note que:
(i) Si,j = para j i
(ii) Adicionamos artificialmente as atividades A0 e AN+1 que respectivamente terminam
antes e comeam depois que qualquer outra atividade. Feito isso, a seleo tima do
problema original claramente S(0, N+1)

Alguma diviso particularmente interessante?

Guloso
Passo 3: Caracterizar a escolha gulosa
Teorema: Sejam i < j e Az a atividade de Si,j que termina primeiro.
Ento:
(i) Az pertence a uma seleo tima de Si,j
(ii) Si,z =
Prova: (ii) Se Si,z , ento h uma atividade Am entre Ai e Az.
Neste caso, Am tambm uma atividade de Si,j e termina primeiro
que Az, contradizendo a escolha de Az.

Guloso
Passo 3: Caracterizar a escolha gulosa
Prova (continuao): (i) Seja S uma seleo tima de Si,j e seja
Ak a atividade que primeiro termina em S. Se k = z, ento de fato o
item (i) verdadeiro. Suponha portanto que k z.
Se z < k, ento ez ek. Logo, S' = S - Ak U Az uma outra seleo
tima de Si,j, mostrando que (i) verdadeiro.
Se k < z, ento ek ez. Como Ak Si,j, para no contradizer a
escolha de Az, temos que ek = ez. Novamente, S' = S - Ak U Az
uma outra seleo tima de Si,j, mostrando que (i) verdadeiro.

Guloso
Passo 4: Computar uma soluo tima de
cada subproblema de forma "top-down"

Guloso
funo SelecaoMaxima(val i, j, s(), e(): Inteiro): Conjunto
incio
zi+1
enquanto s(z) < e(i) E z < j faa z z + 1 fim-enquanto
se z < j ento
retornar ({Az} U SelecaoMaxima(z, j, s, e))
seno
retornar ()
fim-se
fim-funo
ler (N, s(1..N), e(1..N))
e(0), s(N+1) min {s(i) | 1 i N} - 1, e(N) + 1
escrever (SelecaoMaxima(0, N+1, s, e))

Guloso
Passo 5: Transformar o algoritmo recursivo
em iterativo

Guloso
funo SelecaoMaxima(val i, j, s(), e(): Inteiro): Conjunto
var Selecao: Conjunto // implementao por lista ligada
incio
zi+1
enquanto z < j faa
enquanto s(z) < e(i) E z < j faa z z + 1 fim-enquanto
Selecao Selecao {Az}
iz
zi+1
Complexidade de Tempo:
fim-enquanto
O(N2)
retornar (Selecao)
(anlise pode melhorar!)
fim-funo
ler (N, s(1..N), e(1..N))
e(0), s(N+1) min {s(i) | 1 i N} - 1, e(N) + 1
escrever (SelecaoMaxima(0, N+1, s, e))

Guloso
funo SelecaoMaxima(val i, j, s(), e(): Inteiro): Conjunto
var Selecao: Conjunto // implementao por lista ligada
incio
para z i + 1 at j faa
se e(i) s(z) ento
Selecao Selecao {Az}
iz
fim-se
fim-para
retornar (Selecao)
Complexidade de Tempo:
fim-funo
(N)
ler (N, s(1..N), e(1..N))
e(0), s(N+1) min {s(i) | 1 i N} - 1, e(N) + 1
escrever (SelecaoMaxima(0, N+1, s, e))

Cdigos de Huffman

Guloso

Introduo:

Cdigos de Huffman so amplamente usando como


tcnica de compresso de dados sem perdas;
ganhos de 20% at 90% so tpicos

Baseia-se na frequncia de apario dos diferentes


smbolos no arquivo sendo compactado: a smbolos
de maior (resp. menor) frequncia de apario so
atribudos codificaes menores (resp. maiores)

Guloso

Exemplo:

Seja um arquivo com 100.000 caracteres (100 KB),


com a seguinte frequncia de apario de smbolos:

Frequncia (em milhares)

45

13

12

16

Como compactar tal arquivo?

Guloso
Considere recodificar representao dos smbolos: usar um
cdigo de tamanho fixo de 3 bits suficiente para o arquivo em
questo ao invs de 8 bits, ou mesmo usar um cdigo de
tamanho varivel!
a

Codificao tamanho fixo

000

001

010

011

100

101

Codificao tamanho varivel

101

100

111

1101

1100

Usando tamanho fixo de 3 bits: 37,5 KB


Usando tamanho varivel: 28 KB
possvel comprimir mais?

Guloso

Problema: Dado um arquivo com smbolos sobre o


alfabeto , no qual a frequncia do smbolo a
denotada por f(a), determinar a codificao C tima para
tornar o arquivo o menor possvel, isto , definir um cdigo
C(a) para o smbolo a de modo que seja mnima a
expresso:

Guloso

Nem toda codificao desejvel. Exemplo:

C(a) = 1
C(b) = 01
C(c) = 101

no uma codificao desejvel, pois o texto codificado


101 representa um a seguido de um b ou de apenas um
c?

Consideramos apenas codificaes nas quais nenhum


cdigo prefixo de outro (cdigo de prefixos)

Guloso
Observe que um cdigo C pode ser
representado por uma rvore
estritamente binria (i.e., uma
rvore na qual cada n tem 0 ou 2
filhos) tal que:
os smbolos so folhas
cada aresta possui rtulo 0 ou 1
C(x) a sequncia de rtulos
no caminho da raiz at a folha x

a
0

d
0

Guloso
Portanto, o problema de
compactao pode ser reduzido
aquele de determinar uma rvore T
estritamente binria com conjunto
de folhas que minimize:
B(T) = { f(a) |C(a)| : a }

a
0

d
0

Guloso
Passo 1: Caracterizar a subestrutura tima
do problema
Suponha que numa soluo T tima x e y so folhas irms. Seja
T' = T - x - y tal que f(z) = f(x) + f(y) e z pai de x e y. Portanto, T' uma
codificao do alfabeto ' = - {x ,y} U {z}
Note que B(T') = a ' (f(a) |C(a)|) = a -{x,y} (f(a) |C(a)|) + f(z) |C(z)| =
= a -{x,y} (f(a) |C(a)|) + f(x)(|C(x)|-1) + f(y)(|C(y)|-1) =
= a (f(a) |C(a)|) - f(x) - f(y) = B(T) - f(x) - f(y)
Portanto, B(T) = B(T') + f(x) + f(y)
O que dizer de T'?

Guloso
Passo 1: Caracterizar a subestrutura tima
do problema
T' tem que ser codificao tima de '. Caso contrrio, seja T'' uma
codificao tima de ':

Se B(T'') > B(T'), isto contradiria o fato de T'' ser uma codificao
tima de '
Se B(T'') < B(T'), ento seja T''' obtida de T'' colocando-se a e b como
filhos de z com as frequencias originais. Ento:
B(T''') = B(T'') + f(x) + f(y) < B(T') + f(x) + f(y) = B(T), contrariando o
fato de T ser uma codificao tima de

Guloso
Passo 2: Definir o valor de uma soluo
tima de cada subproblema recursivamente
Se soubssemos que x e y so irmos numa soluo tima,
saberamos que:
B(T) = B(T') + f(x) + f(y), onde T' a codificao tima de
' = - {x ,y} U {z} tal que z pai de x e y e f(z) = f(x) + f(y)

Mas no sabemos. O que fazer? Testar todas as possveis


escolhas para x e y

Guloso
Passo 2: Definir o valor de uma soluo
tima de cada subproblema recursivamente
Recurso:
B(T) = f(a) + f(b), se = {a, b}
B(T) = min{ B(Txy) + f(x) + f(y) | x, y , x y }, se | | > 0,
onde Txy a codificao tima de xy = - {x ,y} U {z} tal que
z pai de x e y e f(z) = f(x) + f(y)

Guloso
Passo 3: Caracterizar a escolha gulosa
Teorema: Sejam x, y , x y os caracteres de menor frequncia. Existe
uma codificao tima T em que x e y so folhas irms de T
Prova: Seja T' uma codificao tima. Assuma que x e y no so folhas irms em T. Sejam a, b
duas folhas irms de mxima profundidade em T'. Assuma sem perda de generalidade que f(x) f(y) e
f(a) f(b). Naturalmente, f(x) f(a) e f(y) f(b). Como a, b tem mxima profundidade, ento |C(x)| |C
(a)| e |C(y)| |C(b)|.
Construa T a partir de T' trocando de posio os pares x, a e y, b. Logo:
B(T') - B(T) = (f(a)-f(x))|C(a)| + (f(b)-f(y))|C(b)| + (f(x)-f(a))|C(x)| + (f(y)-f(b))|C(y)| =
= (f(a)-f(x))(|C(a)|-|C(x)|) + (f(b)-f(y))(|C(b)|-|C(y)|)
Como f(a)-f(x) 0, |C(a)|-|C(x)| 0, f(b)-f(y) 0, |C(b)|-|C(y)| 0, ento:
B(T') - B(T) 0. Logo, B(T') = B(T) implicando que T uma rvore tima onde x e y so folhas irms de
T.

Guloso
Passo 4: Computar uma soluo tima de
cada subproblema de forma "top-down"

Guloso
registro ArvoreHuffman
Esq, Dir: ^ArvoreHuffman, f: Inteiro, a: Caracter
fim-registro
var x: ^ArvoreHuffman, Arvores: Conjunto
ler (N)
para i 1 at N faa
alocar (x)
ler (x^.a, x^.f)
Arvores Arvores U {x}
fim-para
Huffman(Arvores) //a partir das folhas cresce a rvore at a raiz
escrever (Arvores) //Arvores agora conjunto unitrio

Guloso
procedimento Huffman(ref Arvores: Conjunto)
var x, y, z: ^ArvoreHuffman
incio
se |Arvores| > 1 ento
x T' tal que T'^.f = min { T^.f | T Arvores }
Arvores Arvores - {x}
y T' tal que T'^.f = min { T^.f | T Arvores }
Arvores Arvores - {y}
alocar(z)
z^.Esq, z^.Dir, z^.f x, y, x^.f + y^.f
Arvores Arvores U {z}
Huffman(Arvores)
fim-se
fim-procedimento

Guloso
Passo 5: Transformar o algoritmo recursivo
em iterativo

Guloso
procedimento Huffman(ref Arvores: Conjunto)
var x, y, z: ^ArvoreHuffman
incio
enquanto |Arvores| > 1 faa
x T' tal que T'^.f = min { T^.f | T Arvores }
Arvores Arvores - {x}
y T' tal que T'^.f = min { T^.f | T Arvores }
Arvores Arvores - {y}
alocar(z)
z^.Esq, z^.Dir, z^.f x, y, x^.f + y^.f
Arvores Arvores U {z}
fim-enquanto
fim-procedimento

Guloso
procedimento Huffman(ref Arvores: Conjunto)
var x, y, z: ^ArvoreHuffman
incio
enquanto |Arvores| > 1 faa
x T' tal que T'^.f = min { T^.f | T Arvores }
Arvores Arvores - {x}
y T' tal que T'^.f = min { T^.f | T Arvores }
Arvores Arvores - {y}
alocar(z)
z^.Esq, z^.Dir, z^.f x, y, x^.f + y^.f
Arvores Arvores U {z}
Complexidade de Tempo:
fim-enquanto
(N.lgN)
(implementando Conjunto com um Heap:
fim-procedimento
Extrair mnimo: O(lgN)
Inserir elemento: O(lgN))

Guloso
Min-Heap:

Construo de N
elementos: O(N.lgN)
Inicializar um heap vazio
para cada elemento x

Propriedade Min-Heap:
se x pai de y, ento x y
(i.e., x.chave y.chave)

dos N elementos, inserir


x no heap
(pode ser feito com um
algoritmo mais eficiente O
(N))

Inserir x: O(lgN)
colocar x como folha mais

direita do ltimo nvel (se


completo, como primeira
folha de um novo nvel)
se x e seu pai y no atendem
a propriedade de min-heap,
trocar x de posio com y.
Repetir este passo at que a
propriedade seja atendida

10

19

11

11

11

Extrair mnimo: O(lgN)


extrair raiz (mnimo valor).
seja x a folha mais direita

22

35

30

44

do ltimo nvel. Extrair x e


coloc-lo como raiz
se x e seus filhos y, z no
atendem a propriedade de
min-heap, trocar x de posio
com min {y, z}. Repetir este
passo at que a propriedade
seja atendida

Exerccios

Guloso
1.

Resolva os problemas a seguir usando o mtodo Guloso. Evidencie as 5 etapas do processo de


criao e calcule a complexidade de tempo do algoritmo resultante.
a.

Dado um conjunto x1 < < xN de pontos da reta real, determine o menor conjunto de
intervalos fechados de tamanho unitrio que contenham todos os pontos. Ex.: para os
pontos 1, 2.2, 3.1, 4.3, 5.3 o conjunto de intervalos { [1;2], [2;3], [3;4], [4;5], [5;6] } contm
todos os pontos, mas no o menor conjunto que consegue fazer isto pela existncia de {
[1;2], [2.1;3.1], [4.3;5.3] }.

b.

H uma srie de N tarefas e uma recompensa de Ri reais se a tarefa i for iniciada no


mximo at a hora Ti para todo 1 i N. Cada uma destas tarefas leva 1h para ser
executada, a primeira tarefa comea na hora zero, e elas podem ser executadas em
qualquer ordem. Faa uma programao das tarefas de modo a maximizar a receita. Ex:
se N = 5, T1 = 1, T2 = 2, T3 = 3, T4 = 3, T5 = 4, R1 = 1, R2 = 2, R3 = 2, R4 = 3, R5 = 1, a
programao de tarefas 4, 3, 2, 1, 5 tem receita 3+2+0+0+0=5, mas no tima pela
existncia da programao 1, 2, 4, 5, 3 com receita 1+2+3+1+0=7.

Guloso
1.

(continuao)
c.

Dado um conjunto de atividades A = {A1, ..., AN}, Ai com horrios de incio si e de fim ei,
determinar a cardinalidade do menor subconjunto S A tal que se p um instante dentro
do horrio de alguma atividade de A, ento p um instante dentro do horrio de alguma
atividade de S.

You might also like