













Estude fácil! Tem muito documento disponível na Docsity
Ganhe pontos ajudando outros esrudantes ou compre um plano Premium
Prepare-se para as provas
Estude fácil! Tem muito documento disponível na Docsity
Prepare-se para as provas com trabalhos de outros alunos como você, aqui na Docsity
Encontra documentos específicos para os exames da tua universidade
Prepare-se com as videoaulas e exercícios resolvidos criados a partir da grade da sua Universidade
Responda perguntas de provas passadas e avalie sua preparação.
Ganhe pontos para baixar
Ganhe pontos ajudando outros esrudantes ou compre um plano Premium
Lista de exercícios sobre grafos. Respostas de exercícios.
Tipologia: Exercícios
1 / 21
Esta página não é visível na pré-visualização
Não perca as partes importantes!














1.a. Não existe grafo não-dirigido, pois o número de vértices de grau ímpar de um Grafo G qualquer, não-dirigido, é sempre par. Existem 7 vértices de grau ímpar(1, 1, 3, 3, 3, 7, 9) 1.b. Não seria possível, pois num momento qualquer de uma festa qualquer, sempre vão existir duas pessoas que apertaram o mesmo número de mãos. Seja n o número de pessoas na festa. Uma pessoa pode apertar, no máximo, n − 1 mãos e, no mínimo, nenhuma. Se num dado momento houver k indivíduos que ainda não cumprimentaram ninguém, o número máximo de apertos de mão que alguém pode ter dado será n − k − 1. Excetuando esses indivíduos que não cumprimentaram ninguém, sobram n−k pessoas, que terão que ser distribuídas entre os n − k − 1 números possíveis de apertos de mão 1, 2, · · · , n − k − 1. Pelo Princípio da Casa de Pombos, duas delas terão dado o mesmo número de apertos de mão. Note que podemos tomar k = 0, obtendo o mesmo resultado. 1.c. Os grafos não são isomorfos. Não compartilham a mesma matriz de adjacência. VERIFICAR A SEQUENCIA DE GRAUS DOS VIZINHOS 2.a Um emparelhamento 𝑀 é um conjunto de arestas com a propriedade de que todo vértice de 𝐺 incide em no máximo uma aresta de 𝑀. Um emparelhamento é perfeito quando todo vértice de 𝐺 incide em exatamente uma aresta de 𝑀. O emparelhamento perfeito contêm no máximo |𝑉 (𝐺)|/2 arestas e também pode-se dizer que emparelhamentos perfeitos só são possíveis quando o grafo possui número par de vértices. 2.b Um caminho M-alternante P para um emparelhamento M é aquele onde as arestas se alternam entre arestas que pertencem a M e arestas que não pertencem a M, isto é, é um caminho tal que as arestas de P estão alternadamente e m A \ M e M. E x e m p l o : A l t e r n a n t e { ( D , H ) , ( F, B ) } e A u m e n t a n t e {A(A,B),C(C,F),D(D,G),H(H,E)} Caminho M-aumentante ou caminho de aumento é um caminho M-alternante onde os extremos (vértices final e inicial) não são saturados pelas arestas de M. Sempre que existir um caminho M-aumentante, o emparelhamento M não será máximo em G, ou seja, haverá um emparelhamento M’ máximo tal que M ⊂ M’. O contrário também é verdadeiro, resultado do Teorema de Berge.
No Grafo em questão, o emparelhamento não é máximo, pois existe um caminho M-aumentante. O Teorema de Berge diz que dado um grafo G e um emparelhamento M em G, M é um emparelhamento máximo se e somente se G não tem nenhum caminho M- aumentante em relação a M. No grado em questão, existe um caminho M’ tal que M ⊂ M’, {D(D,A),B(B,C),F(F,J),H(H,G)}, de forma que M nã é máximo.
A partir da lista de adjacência, iniciamos pela primeira variável, que é por sua vez o primeiro vértice do grafo.
O grafo representa a lista de conhecidos de Zezinho. Cada vértice representa um conhecido e as arestas representam as relações de amizade entre eles. Cada hexágono representa seis pessoas, onde cada pessoa do hexágono conhece outras cinco. Cada grupo(hexágono) compartilha pelo menos uma pessoa conhecida com outro grupo e cinco que não são conhecidas. O grafo será representado por um matriz de adjacências, em que se o convidado i conhecer o convidado j, na matriz de adjacências, o elemento (i,j) terá o valor 1 indicando que os indivíduos se conhecem e 0(zero) caso contrário. O algoritmo recebe como entrada a matriz de adjacências, Mat. Algoritmo(Mat: matriz de adjacências) conhecidos = 0 estranhos = 0 para i variando de 0 até linhas(Mat) faça para j variando de 0 até colunas(Mat) faça se Mat[i,j]=1 faça conhecidos = conhecidos + 1 senão faça se j diferente de i faça estranhos = estranhos + 1 se conhecidos>=5 e resto_divisao(conhecidos,5)=0 e estranhos>= faça
5.b
O problema pode ser resolvido com a utilização do algoritmo de Dijkstra. Dada a matriz de adjacência do grafo, o algoritmo de Dijkstra retorna uma árvore com o caminho mínimo do vértice origem (s) ao destino, no caso da questão, de P1 a P6. O caminho mínimo representa o tempo de troca dos programas. Pseudo-código Enquanto True faça caminho <- Dijkstra(G, s) caminho <- sort(caminho) # ordena a lista retornada pelo algoritmo dijkstra para p em caminho faça execute p Retorno do Algoritmo Prog. 0 3 5 1 4 2 Dist 0 10 10 30 30 40 Segue um passo-a-passo da execução do algoritmo
6.b G: grafo representado como lista de adjacência cost : matriz de distâncias entre os vértices Algoritmo DFS(G, cost, origem, destino) custo[origem] <- 0 visitado : lista vazia path: lista vazia fullpath: lista vazia contapath = 0 retorne VISITA_DFS(origem, destino, path, fullpath, visitado, cost, custototal=0, contapath) Algoritmo VISITA_DFS(origem, destino, visitado, path, fullpath, visitado, cost, custototal, contapath) visitado.push(origem) path.push(origem) se origem==destino faça contapath = contapath + 1 fullpath.push(path) custo = 0 para i em range((tamanho de path)-1) faça custo = custo + cost[path[i]][path[i+1]] custototal.push(custo) para cada v em Adj(G[origem]) faça se v não está em visitado faça VISITA_DFS(v, destino, path, fullpath, visitado, cost, custototal, contapath) path.pop() visitado.pop() se path é vazio faça retorne fullpath do min(custototal) origem = 0 # vértice de origem, no caso, Zezinho retorno = lista vazia # lista qye retorna os ca minhos mínimos de Zezinho a todos os outros vértices para i=origem aré Nr.Vértices-1 faça: amigos = DFS(G, cost, origem, i+1)
retorno.push(amigos) pares: lista vazia #armazena os amigos distantes da rede de amigos de Zezinho para path em retorno faça: if tamanho(path)>2 faça pares.push(ultimo elemento de path) para i=0 até tamanho(pares)-1 faça para j =i+1 até tamanho(pares) faça left = DFS(G, cost, origem, pares[i]) # retorna um vértice distante de Zezinho, aqui nomeado de left (relativo a i) right = DFS(G, cost, origem, pares[j]) # retorna um vértice distante de Zezinho, aqui nomeado de right (relativo a j) se tamanho(left)==tamanho(right) faça left.pop() right.pop() se left!=right faça Imprima ‘Par (pares[i], pares[j]) atende aos requisitos senão faça Imprima ‘Não existe par que atenda aos requisitos’ Código Python com exemplo import sys import numpy as np custo = [0]* def dfs(gr, cost, origem, destino): custo[origem]= return visita_dfs(origem, destino, [], [], [], cost, [], 0)
def visita_dfs(origem, destino, path, fullpath, visitado, cost, custoTotal, contapath): visitado.append(origem) path.append(origem) if origem==destino: contapath+= fullpath.append(list(path)) custo = 0 for i in range(len(path)-1):
for path in retorno: if len(path)>2: pares.append(path[-1]) for i in range(len(pares)-1): for j in range(i+1,len(pares)): left = dfs(gr, cost, origem, pares[i]) right = dfs(gr, cost, origem, pares[j]) if len(left)==len(right): left.pop() right.pop() if left!=right: print('Par:(%s,%s) atende aos requisitos'%(pares[i], pares[j])) else: print('Não existe par que atenda aos requisitos') 7.a O algoritmo de Kosaraju baseia-se na definição de que um Grafo G e o seu transposto T(G) possuem exatamente os mesmos componentes fortemente conectados. Se aplicar a alteração proposta por Zezinho, o algoritmo não funcionará corretamente. A segunda busca vai executar os mesmos passos da primeira busca e não vai identifica os componentes fortemente conectados. Considere o grafo abaixo como exemplo. Passos do Kruskal original Primeiro DFS Visita(0): 0 1 2 3 Pilha(0): 3 Visita(1): 4 5 6 7 Pilha(1): 7 Visita: 0 1 2 3 4 5 6 7 Pilha: 3 7 Como 6 5 4 3 2 1 e 0 já foram visitados, empilha-os Pilha: 3 7 6 5 4 2 1 0
Segundo DFS Inverte o grafo Começa no topo da pilha. Atravessa todos os filhos do vértice. Se o vértice já tiver sido visitado, um componente fortemente conectado foi alcançado. Pilha: 3 7 6 5 4 2 1 0 Começando do 0 , visita os vértices 1 2 e 3 em sequência. Como o filho do vértice 3 já foi visitado, alcançou um componente fortemente conectado. Visita(0): 0 1 2 3 Pilha(0): 3 7 6 5 4 2 1 SCC(0) 0 1 2 3 Vai ao topo da pilha e retira os vértices já visitados Escolhe o topo da pilha(4) o retira da pilha e visita todos os filhos(5 e 6). Pilha(1): 3 7 6 5 Visita(1): 0 1 2 3 4 5 6 SCC: 4 5 6 Ao encontrar um vértice já visitado, alcançou um componente fortemente conectado (4 5 6). Vai ao topo da pilha e retira os vértices já visitados Pilha(2): 3 7 Visita o topo da pilha (7) e o retira da pilha. Visita os filhos do vértice. No caso, não tem mais filhos, retorna um componente fortemente conectado. SCC: 7 Vai ao topo da pilha e retira os vértices já visitados. A pilha fica vazia. Pilha: [] Passos do Kruskal sugerido por Zezinho Primeiro DFS Visita(0): 0 1 2 3 Pilha(0): 3 Visita(1): 4 5 6 7 Pilha(1): 7 Visita: 0 1 2 3 4 5 6 7 Pilha: 3 7 Como 6 5 4 3 2 1 e 0 já foram visitados, empilha-os
8.b Sim, é possível. Kruskal
9.a cost : matriz de distâncias entre os vértices Algoritmo DFS(G, cost, origem, destino) custo[origem] <- 0 visitado : lista vazia path: lista vazia fullpath: lista vazia imprime VISITA_DFS(origem, destino, path, fullpath, visitado, cost, custototal) Algoritmo VISITA_DFS(origem, destino, visitado, path, fullpath, visitado, cost, custototal) visitado.push(origem) path.push(origem) se origem==destino faça fullpath.push(path) custo = 0 para i em range((tamanho de path)-1) faça custo = custo + cost[path[i]][path[i+1]] custototal.push(custo) para cada v em Adj(G[origem]) faça se v não está em visitado faça VISITA_DFS(v, destino, path, fullpath, visitado, cost, custototal) path.pop() visitado.pop() se path é vazio faça retorne fullpath do max(custototal), max(custototal) origem= Para i=origem até Nr.Vértices-1 faça imprima ‘Origem:’, origem, ‘->Destino:’, i+ DFS(G, cost, origem, i+ 1 ) Código Python Exemplo import sys
9.b O algoritmo proposto não funcionará corretamente, pois o algoritmo de. Bellman-Ford assume que não existe ciclo negativo. Ao se efetuar a alteração proposta pode ocorrer ciclo negativo o que invalida a execução do algoritmo. 9.c Algoritmo FloydWarshall ( G ) Entrada digrafo ponderado G = ( V , E ) Saída matriz de distâncias D i ← 1 para cada v ∈ V faça rotule v como vi i ← i + 1 D^0 ← W para k ← 1 até n faça para i ← 1 até n faça para j ← 1 até n faça
algoritmo comentada
se** Dk -1[ i ][ k ] + Dk -1[ k] [ j ] < Dk -1[ i ][ j ] então Dk -1[ i ][ j ] = Dk -1[ i ][ k ] + Dk -1[ k ][ j ] para i ← 1 até n faça se ( D^0 [i][i] < 0) então return True return False Se retornar True é porque existe ciclo negativo
Para encontrar o fluxo máximo, usa-se o algoritmo de Ford-Fulkerson Algoritmo FordFulkerson ( N ) Entrada: rede de fluxo N = ( G , c , s , t ), onde G = ( V , E ) Saída: um fluxo máximo f para N para cada ( u , v ) ∈ E faça f [ u , v ] ← 0 //guarda o valor do fluxo do arco de u a v continua ← VERDADEIRO enquanto ( continua ) faça //laço principal π ← CAM_AUM_DFS( N , f ) //busca via DFS um caminho de aumento se ( π != NIL) //existe um caminho de aumento na rede residual ∆ ← ∞ para cada ( u , v ) ∈ π faça se ( ∆ f( u , v ) < ∆ ) ∆ ← ∆ f( u , v ) //computa a capacidade residual do caminho //atualiza o valor da capacidade residual //adiciona o fluxo residual aos arcos do caminho //retorna verdadeiro se o arco for de avanço para cada ( u , v ) ∈ π faça se (ARCO_AVANCO( u , v )) f [ u , v ] ← f [ u , v ] + ∆ senão f [ u , v ] ← f [ u , v ] - ∆ senão continua ← FALSO retorna f A seguir, executamos o passo-a-passo do algoritmo de Ford-Fulkerson para resolver o problema do fluxo máximo da questão 10. O conjunto de bondes críticos é b-c-a-d