Logo Passei Direto
Buscar
Material
páginas com resultados encontrados.
páginas com resultados encontrados.
details

Libere esse material sem enrolação!

Craque NetoCraque Neto

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

details

Libere esse material sem enrolação!

Craque NetoCraque Neto

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

details

Libere esse material sem enrolação!

Craque NetoCraque Neto

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

details

Libere esse material sem enrolação!

Craque NetoCraque Neto

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

details

Libere esse material sem enrolação!

Craque NetoCraque Neto

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

details

Libere esse material sem enrolação!

Craque NetoCraque Neto

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

details

Libere esse material sem enrolação!

Craque NetoCraque Neto

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

details

Libere esse material sem enrolação!

Craque NetoCraque Neto

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

details

Libere esse material sem enrolação!

Craque NetoCraque Neto

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

details

Libere esse material sem enrolação!

Craque NetoCraque Neto

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

Prévia do material em texto

Q1: Seja G(V, E) o grafo simples não-direcionado e ponderado cuja matriz de 
adjacências é dada abaixo (células em branco indicam arestas inexistentes) 
1 2 3 4 5 
 
 1 4 2 5 
 2 60 13 7 
 3 18 20 
 4 1 
 5 
 
 a) Simule a execução do algoritmo de Kruskal para árvore geradora 
mínima, indicando a cada passo qual a decisão tomada. 
Inicialmente, temos uma floresta com 5 árvores triviais (cada uma com apenas um único 
vértice). Passaremos a olhar as arestas em ordem crescente de custo. 
1a iteração: aresta 4,5 é adicionada pois não fecha ciclo (4 e 5 estão em árvores distintas) 
2a iteração: aresta 1,3 é adicionada pois não fecha ciclo (1 e 3 estão em árvores distintas) 
3a iteração: aresta 1,2 é adicionada pois não fecha ciclo (1 e 2 estão em árvores distintas) 
4a iteração: aresta 1,5 é adicionada pois não fecha ciclo (1 e 5 estão em árvores distintas) 
A árvore geradora formada pelos 5 vértices do grafo e pelas 4 arestas acima é retornada. 
Obs.: Uma estrutura do tipo UNION/FIND para conjuntos disjuntos é utilizada para 
responder se dois vértices estão na mesma árvore, tornando desnecessária uma busca a cada 
aresta que é considerada. Inicialmente, chamamos CREATE_SET(v) para todo vértice v. 
Depois, a cada aresta v,w considerada, perguntamos se FIND(v) != FIND(w). Se forem 
diferentes, podemos adicionar a aresta, após o que chamamos UNION(v, w); se já forem 
iguais, a aresta será simplesmente “pulada". 
 b) Simule a execução do algoritmo de Prim para árvore geradora mínima, 
indicando a cada passo qual a decisão tomada. 
Inicialmente, escolhemos um vértice para iniciar o algoritmo. Começaremos então pelo 
vértice 1, que será colocado e uma heap binária de mínimo usada como estrutura. Esse 
vértice terá na heap prioridade 0, significando que ele poderá entrar na árvore que será 
Algoritmos e Grafos – Lista 1
construída pagando para isso um custo 0 de entrada (pois será o primeiro vértice a entrar, 
não sendo portanto conectado a nenhum outro que lá já estivesse). 
1a iteração: removemos a raiz da heap, que é o vértice 1, adicionamos 1 à árvore que 
estamos montando, marcamos 1 como já presente na árvore, e a seguir iteramos pelos 
vizinhos do vértice 1: os vizinhos do 1 que ainda não estão presentes na árvore são os 
vértices 2, 3 e 5. Colocamos então esses vértices na heap, associados respectivamente às 
prioridades 4, 2 e 5, que são os custos que eles teriam para entrar na árvore sendo 
conectados ao vértice 1. 
2a iteração: removemos a raiz da heap, que é o vértice 3, adicionamos 3 à árvore que 
estamos montando (ligado ao vértice 1 pela aresta 1,3 de custo 2), marcamos 3 como já 
presente na árvore, e a seguir iteramos pelos vizinhos do vértice 3: os vizinhos do 3 que 
ainda não estão presentes na árvore são os vértices 2, 4 e 5. O vértice 2 já está na heap com 
prioridade 4, então nada faremos (já que o custo de entrar na árvore se conectando ao 
vértice 3 seria de 60, que é pior que o custo atual indicado na heap que é 4); o vértice 4 
ainda não está na heap, e será portanto acrescentado, associado à prioridade 18, que é o 
custo que ele teria ao entrar na árvore conectado ao vértice 3); finalmente, o vértice 5 já 
está na heap com prioridade 5, então nada faremos (já que o custo de entrar na árvore se 
conectando ao vértice 3 seria de 20, que é pior que o custo atual indicado na heap que é 5). 
3a iteração: removemos a raiz da heap, que é o vértice 2, adicionamos 2 à árvore que 
estamos montando (ligado ao vértice 1 pela aresta 1,2 de custo 4), marcamos 2 como já 
presente na árvore, e a seguir iteramos pelos vizinhos do vértice 2: os vizinhos do 2 que 
ainda não estão presentes na árvore são os vértices 4 e 5. O vértice 4 já está na heap com 
prioridade 18, então baixaremos essa prioridade agora para 13, que é o custo que ele teria 
ao entrar na árvore conectado ao vértice 2; o vértice 5 já está na heap com prioridade 5, 
então nada faremos (já que o custo de entrar na árvore se conectando ao vértice 2 seria de 7, 
que é pior que o custo atual indicado na heap que é 5). 
4a iteração: removemos a raiz da heap, que é o vértice 5, adicionamos 5 à árvore que 
estamos montando (ligado ao vértice 1 pela aresta 1,5 de custo 5), marcamos 5 como já 
presente na árvore, e a seguir iteramos pelos vizinhos do vértice 5: o único vizinho do 5 que 
ainda não está presente na árvore é o vértice 4. O vértice 4 já está na heap com prioridade 
13, então baixaremos essa prioridade agora para 1, que é o custo que ele teria ao entrar na 
árvore conectado ao vértice 5. 
5a iteração: removemos a raiz da heap, que é o vértice 4, adicionamos 4 à árvore que 
estamos montando (ligado ao vértice 5 pela aresta 4,5 de custo 1), marcamos 4 como já 
presente na árvore, e a seguir iteramos pelos vizinhos do vértice 4: não há vizinhos de 4 que 
ainda não estejam na árvore, então nada a fazer. 
Como a heap nesse momento encontra-se vazia, o algoritmo termina, retornando a árvore 
que foi ao longo dele construída. 
Q2: Para qual das operações abaixo a representação de grafos no computador via 
matriz de adjacências é mais eficiente do que a representação via listas de 
adjacências? 
 a) percorrer cada uma das arestas do grafo em qualquer ordem 
Matriz é MENOS eficiente que listas, porque precisarei olhar a matriz inteira com custo 
O(n^2). Percorrer as listas de vizinhos tem custo O(n+m). 
 b) obter o grau médio dos vértices do grafo 
Matriz é MENOS eficiente que listas, porque novamente precisarei olhar a matriz inteira 
com custo O(n^2). Pegar o tamanho de cada lista pode ser feito em tempo O(1), então 
calcular o grau médio de todos os vértices seria feito em tempo O(n). 
 c) checar se cinco vértices dados constituem um subgrafo completo (todos 
vizinhos de todos) 
Matriz é MAIS eficiente que listas, porque isso seria feito em tempo constante, ou seja, 
O(1), bastando verificar Combinação(5, 2) = 10 células na matriz. Fazer isso em listas de 
adjacências exige que eu itere pelas listas de vizinhos de alguns vértice, algo que pode ter 
custo O(grau máximo do grafo), e que no pior caso pode ser até mesmo linear no número 
de vértices, ou seja, O(n). Essa seria, portanto, a resposta correta. 
 d) localizar o vértice com o maior número de vizinhos de grau menor 
do que 4 
Matriz é MENOS eficiente que listas, porque precisarei no mínimo olhar a matriz inteira 
com custo Omega(n^2), isto é, tenho já um limite inferior quadrático para meu esforço 
total. Fazendo com listas, posso simplesmente iterar pelas listas de vizinhos de cada vértice, 
algo que pode ser feito em tempo O(n+m), já que para cada vizinho olhado posso apenas 
perguntar seu grau em tempo O(1), pois é uma mera consulta ao tamanho de sua lista de 
vizinhos. Então isso já é melhor que o limite INFERIOR para o caso em que uso matriz. 
 e) nenhuma das respostas acima (NRA) 
Q3: Sejam A1 e A2 dois algoritmos para um mesmo problema, que recebem como 
entrada um grafo. Se A1 roda em tempo O(n + m log n) e A2 roda em tempo O(n2), 
onde n é o número de vértices do grafo e m é o número de arestas do grafo, 
podemos afirmar que: 
 a) a execução de A1 leva menos tempo do que a execução de A2 para todo 
grafo acíclico 
Ok, se o grafo é acíclico sabemos que mportanto que A2, que é O(n^2). 
 c) quando o número de vértices é suficientemente grande, A2 sempre 
rodará em mais tempo do que A1 
Isso não é verdade. Vai depender da densidade do grafo, isto é, da relação entre m e n. Se m 
for grande, digamos m = n^2, então A2 mais eficiente, necessariamente rodando em menos 
tempo que A1 a partir de certo tamanho da entrada. 
 d) quando o número de vértices é suficientemente grande e o grau mínimo 
do grafo é metade do número de vértices do grafo, A2 sempre rodará em mais 
tempo do que A1 
Se o grau MÍNIMO é n/2, isso significa que o número de arestas desse grafo é quadrático 
em n, isto é, m = Theta(n^2). Portanto, a complexidade de A1 fica O(n^2 log n). E a frase 
fica claramente falsa. 
 e) NRA 
Esta seria a opção correta: “nenhuma das respostas acima”. 
Q4: Seja o grafo simples não-direcionado G(V,E), onde 
 V = {a,b,c,...,h}, 
 E = {ab, ac, ah, bc, bd, bf, bg, cg, df, ef}. 
a) Desenhe uma árvore de largura de raiz a para G, classificando os 
diferentes tipos de arestas encontradas. 
 
 __ a __ 
 / | \ bc: aresta-irmão 
 b ….. c h df: aresta-irmão 
 / | \ : cg: aresta-tio 
 d …f g todas as outras: arestas de árvore 
 | (ou aresta-pai) 
 e 
 
b) Dê as profundidades de entrada e saída de cada um dos vértices de V 
quando uma busca em profundidade de raiz a é executada em G. 
 …. a _________ PE(a) = 1 PS(a) = 8 
 : | \ PE(b) = 2 PS(b) = 6 
 : b ::::::: . h PE(c) = 3 PS(c) = 2 
 : | \ : : PE(d) = 5 PS(d) = 5 
 :… c d : : PE(e) = 7 PS(e) = 3 
 | | : : PE(f) = 6 PS(f) = 4 
 g f …..: : PE(g) = 4 PS(g) = 1 
 : | : PE(h) = 8 PS(h) = 7 
 : e : 
 : ………….: 
c) Utilize a busca em profundidade do item anterior para determinar os 
vértices “demarcadores”, segundo o algoritmo para determinação de 
articulações e componentes biconexas de um grafo. 
 low(a) = a DEMARC (trivial, raiz) 
 low(b) = a DEMARC (trivial, filho da raiz) 
 low(c) = a 
 low(d) = b DEMARC 
 low(e) = e DEMARC 
 low(f) = b 
 low(g) = b 
 low(h) = h DEMARC (trivial, filho da raiz) 
 
d) Que vértices são articulações de G? 
 Articulações: 
 a (pois é a raiz e tem mais de um filho) 
 b (pois não é a raiz e tem um filho demarcador) 
 f (pois não é a raiz e tem um filho demarcador) 
e) Quais são as componentes biconexas de G? 
 
 {e, f} descoberta quando visitamos a aresta de árvore f,e — momento em que 
 vamos desempilhando da pilha auxiliar todos os vértices até o 
 demarcador “e” (inclusive), anotando esse(s) vértice(s) numa 
 componente juntamente com a própria articulação “f" 
 {d, f, b} descoberta quando visitamos a aresta de árvore b,d — momento em que 
 vamos desempilhando da pilha auxiliar todos os vértices até o 
 demarcador “d” (inclusive), anotando esse(s) vértice(s) numa 
 componente juntamente com a própria articulação “b” 
 {h, a} descoberta quando visitamos a aresta de árvore a,h — momento em que 
 vamos desempilhando da pilha auxiliar todos os vértices até o 
 demarcador “h” (inclusive), anotando esse(s) vértice(s) numa 
 componente juntamente com a própria articulação “a” (note que “h" 
 era o SEGUNDO filho da raiz, por isso fizemos esse procedimento) 
 {b, c, g, a} descoberta quando o algoritmo chega ao fim — é tudo o que sobrou 
 na pilha auxiliar 
 
Q5: Verdadeiro ou Falso? 
a) Se uma aresta uv é aresta de retorno em uma busca de profundidade feita em 
um grafo simples não-direcionado G, então uv será aresta de retorno em toda 
busca em profundidade feita em G. 
Claramente falso. Pode tranquilamente ser uma aresta de árvore, dependendo da raiz que se 
escolha para a busca e da ordem em que os vizinhos de cada vértice são considerados 
durante a busca. 
Exemplo: a …. 
 | : 
 b : 
 | : ac é aresta de retorno aqui, mas 
 c … : 
 b …. 
 | : 
 a : 
 | : ac é aresta de árvore aqui, para o mesmo grafo! 
 c … : 
 
b) Se uma aresta uv é aresta de retorno em uma busca de profundidade feita em 
um grafo simples não-direcionado G, então uv será aresta-pai ou aresta-irmão em 
toda busca em largura feita em G. 
 Também falso, embora o contra-exemplo seja ligeiramente menos óbvio. 
 ….. a 
 : | 
 : b 
 : | 
 : c …. 
 : | : 
 :…. d : 
 | : ec é aresta de retorno aqui, mas 
 e …: 
 a 
 / \ 
 b .. d 
 | : | 
 c :….e ec é aresta-primo aqui, para o mesmo grafo! 
c) Se um grafo simples não-direcionado G é acíclico e desconexo, então o número 
de células não-nulas em uma matriz de incidências para G será menor que o 
número de células não-nulas em uma matriz de adjacências para G. 
Falso. Será igual a 2m nos dois casos. Na verdade, no caso da matriz de adjacências, 
podemos fazer com que esse número seja igual a m, se impusermos que toda aresta x,y 
estará representada apenas na cela M[x][y] da matriz M, se x b é um contra-exemplo. Admite apenas uma topsort: a, b. 
 
Q6: Seja o digrafo D(V,E), onde: 
 V = {a,b,c,d,e} 
 E = {ab, ae, bc, bd, be, ce, dc, de}. 
 a) Encontre uma ordenação topológica para D. 
 a, b, d, c, e 
 b) Quantas arestas possui o menor subgrafo de D que só admite 
ordenações topológicas iniciadas em a e terminadas em e? 
 Uma aresta. Trata-se do subgrafo a —> e com dois vértices e uma aresta. 
 c) Quantas arestas possui o menor subgrafo gerador de D que só admite 
ordenações topológicas iniciadas em a e terminadas em e? 
 Se é gerador precisa ter todos os vértices do grafo original. Para que toda topsort tenha que 
começar em a, precisamos que todosos demais vértices tenham alguma aresta de entrada 
(ou seja, não sejam fontes no nosso subgrafo gerador). Portanto, 4 arestas é um limite 
inferior para a resposta que procuramos. Se conseguirmos um exemplo de subgrafo gerador 
em que todas as topsorts comecem em "a" e terminem em “e”, saberemos que temos uma 
solução ótima (ou seja, saberemos que podemos parar de procurar). 
Esse é o caso do subgrafo gerador seguinte: 
a —> b —> d —> c —> e. 
Quatro arestas. Apenas uma top sort (que começa em “a" e termina em “e”). 
Q7: Preciso ir de bicicleta da cidade A à cidade B, cidades estas ligadas por uma 
única estrada. Carrego na bicicleta um bidon (também conhecido como 
caramanhola, vulgo garrafinha) de 600 mililitros, e preciso ingerir 100 mililitros de 
água a cada 5 Km pedalados, do contrário desidrato e morro. Carrego comigo um 
mapa que indica a localização exata dos postos de combustível ao longo daquela 
estrada, locais esses onde posso beber água e encher minha garrafinha. 
 a) Dê um algoritmo guloso que garanta uma viagem com número mínimo 
de paradas para abastecimento. 
 Algoritmo: a partir da posição inicial na cidade A, a próxima parada será no posto 
mais longe possível da parada anterior que não exceda 35 Km de distância da parada 
anterior. (Sempre que minha garrafinha esvaziar, posso ainda percorrer 5 km, portanto 30 
Km + 5 Km = 35 Km é minha autonomia máxima.) 
 Em pseudo-código. 
 Entrada: um array com as posições de n postos ao longo da estrada, e a posição da 
cidade B. (A cidade A é assumida como estando na posição zero.) 
 Saída: os índices dos postos em que devo parar para abastecer. 
 stop_indexes = [ ] 
 current_pos = 0 
 next_stop_index = 0 
 pos[n+1] = pos[B] 
 i = 1 
 while i 35: 
 if next_stop_index == 0: 
 fail! // não há forma possível de completar o trajeto sem morrer 
 stop_indexes += [next_stop_index] 
 current_pos = pos[next_stop_index] 
 else: 
 next_stop_index = i 
 i++ 
 return stop_indexes 
 
 Complexidade: O(n). 
 
 
 b) Prove a corretude de seu algoritmo usando argumento do tipo cut and 
paste. 
Seja A a saída fornecida pelo algoritmo guloso do item (a) para alguma instância de entrada 
que tenha conjunto viável não-vazio (se o conjunto viável for vazio, o algoritmo certamente 
indicará esse caso corretamente). Suponha, para efeito de contradição, que a solução A = 
[p1, p2, …, pk], k>=0, dada pelo algoritmo, embora claramente viável (por construção), 
não seja uma solução ótima para aquela instância do problema. Vamos agora inferir uma 
contradição. 
Seja S = {T_i}, 0 0 soluções ótimas para aquela 
instância, e seja T* dentre essas aquela que maximiza j, o índice da primeira divergência 
com a solução A. Isto é, T* contém p1, p2, …, p(j-1), mas não contém pj, e nenhuma outra 
solução em S contém prefixo de A com tamanho maior do que j-1. A figura abaixo ilustra 
esse prefixo comum entre A e T*, e o X vermelho indica que pj não está presente na 
solução T*. 
 p1 p2 p3 p(j-1) pj p(j+1) … pk 
A: ____|_______|_____|_____…_______|__________|_________|_____ …______|_____ 
T*:____|_______|_____|_____…_______|______|___X______|_______|__…___|_______ 
 q1 q2 q3 qm 
Note que T* tem outros pontos de parada, à direita de pj, possivelmente coincidentes, mas 
não necessariamente coincidentes com pontos de parada da solução A. Note, também, que é 
necessário que exista um ponto de parada, na solução T*, entre a posição de p(j-1) e a 
posição de pj, uma vez que, pela construção gulosa do algoritmo, pj é o ponto mais distante 
à direta de p(j-1) que pode ser atingido, a partir de p(j-1), sem que o ciclista desidrate. Não 
seria possível, portanto, que na solução T* o ciclista reabastecesse em p(j-1) e depois 
apenas em algum posto à direita de pj. Chamaremos esse ponto de parada entre p(j-1) e pj, 
existente em T*, de q1. 
Agora vamos construir T’ da seguinte maneira: primeiramente, copiamos todos os pontos 
de parada de T* para T’, e forçamos a inclusão do ponto de parada pj nesta solução T’que 
estamos construindo, como mostra a figura abaixo. 
 p1 p2 p3 p(j-1) pj 
 ____|_______|_____|_____…_______|______|____|______|_______|__…___|_______ 
 q1 q2 q3 qm 
Neste ponto, T’ está claramente com um ponto de parada a mais em relação a T*, e, 
portanto, não é uma solução ótima (embora certamente viável). O que faremos agora será 
removermos de T’ justamente o ponto de parada q1. Para isso, precisamos garantir que é 
possível remover esse ponto de parada sem afetar a viabilidade da solução. Ora, remover 
esse ponto de parada tem dois efeitos: (1) após reabastecer em p(j-1), o ciclista seguindo a 
solução T’ vai reabastecer apenas em pj, e não em q1, como teria feito se estivesse seguindo 
a solução T*; e (2) o ciclista chegará em q2 tendo reabastecido em pj, e não em q1. O efeito 
1 é claramente ok, pois sabemos que a distância de p(j-1) a pj é percorrível pelo ciclista, 
uma vez que o ciclista precisa percorrê-la quando segue a solução (viável) A. O efeito 2 
também não fere a viabilidade de T’, uma vez que a distância de pj a q2 será menor do que 
a distância de q1 a q2, que, por sua vez, é percorrível. Chegamos assim à solução viável T’ 
mostrada abaixo: 
 p1 p2 p3 p(j-1) pj 
T’:____|_______|_____|_____…_______|__________|______|_______|__…___|_______ 
 q2 q3 qm 
Finalmente, a solução T’ acima é viável, como já argumentado, e é também ótima, uma vez 
que tem exatamente a mesma quantidade de paradas de T* (pois apenas entrou pj e saiu 
q1). Ocorre que o tamanho do prefixo comum de T’ e de A, sendo maior ou igual a j, é pelo 
menos uma unidade maior que o do prefixo comum de T* e de A, que é j-1. E isso é uma 
contradição, pois T* tinha sido escolhida como sendo a solução ótima que maximizava o 
tamanho do prefixo comum com A. 
Sendo assim, a solução A precisa ser ótima.

Mais conteúdos dessa disciplina