CES-11 ALGORITMOS E ESTRUTURAS DE DADOS Aulas Práticas - 2016 Capítulo V Grafos O presente trabalho corresponde ao laboratório do Exame de CES-11/2016 Além deste trabalho, o referido exame terá ainda uma prova feita em sala de aula Para compor a nota do Exame, o peso do trabalho será igual ao peso da prova Objetivos deste laboratório: 1. Ler e armazenar um digrafo numa estrutura com listas de adjacências O digrafo deve ter uma informação inteira e positiva em cada um de seus vértices (duração da tarefa representada pelo vértice) 2. Testar se o digrafo armazenado é cíclico ou acíclico 3. Se o digrafo for acíclico, encarando-o como o cronograma das tarefas de um projeto: Calcular o tempo mínimo de término desse projeto Encontrar as tarefas e os arcos dos caminhos críticos do digrafo (pode haver mais de um) 4. Se for cíclico, encontrar os vértices e os arcos de cada um de seus componentes fortemente conexos Observação: o programa elaborado deverá ler os dados de vários digrafos e, para cada um, realizar os 4 passos anteriores Exercício 5.1: Leitura e armazenamento de digrafos Fazer um programa para: Ler um digrafo, com uma informação inteira e positiva em cada um seus vértices (duração da tarefa representada pelo vértice) Armazenar o digrafo numa estrutura com listas de adjacências Imprimir todo o conteúdo de sua estrutura com lista de adjacências Declarações para digrafos: 15 12 typedef typedef typedef typedef 2 10 int vertice; struct CelulaAdj CelulaAdj; 20 struct CelulaVertice CelulaVertice; struct Grafo Grafo; struct CelulaAdj { vertice vert; CelulaAdj *prox; }; G 1 15 2 12 3 10 4 12 5 20 EspacoVertices 1 12 3 4 5 2 5 4 4 1 5 3 nvert 5 struct CelulaVertice { int duracao; CelulaAdj *listadj; }; 15 12 2 struct Grafo { CelulaVertice *EspacoVertices; int nvert; }; /* Variaveis globais */ Grafo G; A ser alocado dinamicamente (malloc) G 10 20 1 15 2 12 3 10 4 12 5 20 EspacoVertices 1 12 3 4 5 2 5 4 4 1 5 3 nvert 5 /* Prototipos de funcoes */ O grafo a ser lido e escrito é G (variável global) void LerGrafo (void); void ImprimirGrafo (void); /* Programa Principal: */ void main () { printf ("Leitura do Grafo G:\n\n"); LerGrafo (); printf ("\nGrafo G em fase inicial\n"); ImprimirGrafo (); printf ("\n\n"); system ("pause"); return 0; } Observação: no Capítulo VI das aulas teóricas, há um algoritmo para leitura e armazenamento de grafos em listas de adjacências Ele pode ser adaptado para este trabalho Exemplo: para o digrafo Possível conteúdo impresso: 15 12 Grafo G em fase inicial 2 10 1 12 3 4 Numero de vertices: 5 1) duracao: 15 adjs : 2, 4 20 2) duracao: 12 adjs : 5 3) duracao: 10 adjs : 1, 4 4) duracao: 12 adjs : 5 5) duracao: 20 adjs : 3 G 1 15 2 12 3 10 4 12 5 20 EspacoVertices 5 2 5 4 4 1 5 3 nvert 5 Exercício 5.2: Construção da lista de contra-adjacências de cada vértice (substitui o grafo reverso) Contra-adjacente de um vértice v é um vértice do qual v é adjacente No programa anterior: Acrescentar o campo listcontradj ao tipo CelulaVertice struct CelulaVertice { int duracao; CelulaAdj *listadj, *listcontradj; }; e formar a lista de contra-adjacências de cada vértice do grafo Exemplo: no grafo anterior 15 12 1 10 2 3 sua estrutura com as contra-adjacências 15 2 4 3 5 2 12 3 10 1 4 12 5 5 5 20 G EspacoVertices 1 4 1 3 2 4 5 20 1 12 3 4 nvert 5 Exercício 5.3: Teste de aciclicidade para digrafos No programa do exercício anterior, testar se o digrafo lido é cíclico ou acíclico Para tanto procurar arcos de volta no digrafo durante uma busca em profundidade; encontrando um, o grafo é cíclico, caso contrário, é acíclico Observação: no Capítulo VII das aulas teóricas, há um algoritmo para realizar o teste de aciclicidade – pode-se adaptá-lo Lá usa-se uma variável global aciclico para denotar a aciclicidade, mas neste exercício essa variável deve ser um campo da struct grafo Acrescentar o campo aciclico ao tipo grafo: struct Grafo { 12 CelulaVertice *EspacoVertices; int nvert; logic aciclico; }; 1 15 2 2 4 3 5 3 10 1 4 12 5 5 G EspacoVertices 1 3 2 4 5 aciclico 4 1 12 3 20 12 20 1 10 2 5 15 False 3 4 nvert 5 Teste de aciclicidade para digrafos Manter, para o vértice que está sendo visitado, uma pilha com todos os seus ancestrais, incluindo ele próprio Partindo dele, para visitar seus adjacentes, se um deles, já visitado, pertencer a tal pilha, existe o arco de volta Quando todos os seus adjacentes já tiverem sido visitados, retirá-lo da pilha de ancestrais Caso seja encontrado um ciclo, marcar o campo aciclico de G como sendo FALSE Acrescentar o campo visit ao tipo CelulaVertice: struct CelulaVertice { int duracao; logic visit; CelulaAdj *listadj, *listcontradj; }; Declarações, protótipos e funções para pilhas de vértices: typedef struct noh noh; typedef noh *pilha; typedef noh *pontpilha; struct noh {vertice elem; noh *prox;}; void Empilhar (vertice, pilha*); void Desempilhar (pilha*); vertice Topo (pilha); void InicPilha (pilha*); logic Vazia (pilha); void InicPilha (pilha *P) { *P = NULL;} logic Vazia (pilha P) { if (P == NULL) return TRUE; else return FALSE; } void Empilhar (vertice x, pilha *P) { noh *temp; temp = *P; *P = (noh *) malloc (sizeof (noh)); (*P)->elem = x; (*P)->prox = temp; } É necessária ainda uma função para procurar um vértice na pilha de ancestrais void Desempilhar (pilha *P) { noh *temp; if (! Vazia (*P)) { temp = *P; *P = (*P)->prox; free (temp); } else printf ("\n\tDelecao em pilha vazia\n"); } vertice Topo (pilha P) { if (! Vazia (P)) return P->elem; else printf ("\n\tTopo de pilha vazia\n"); return -1; } Exercício 5.4: Se o digrafo for acíclico: Encará-lo como o cronograma das tarefas de um projeto Encontrar e imprimir o tempo mínimo de término de cada tarefa e do projeto como um todo Marcar os vértices e os arcos pertencentes aos caminhos críticos do digrafo, imprimindo-os Sugestão: implementar o algoritmo e a estratégia apresentados no Capítulo VII das aulas teóricas Poderão ser acrescentados novos campos às structs CelulaVertice e CelulaAdj Exercício 5.5: Se o digrafo for cíclico: Encontrar os seus componentes fortemente conexos Numerar cada vértice e cada arco do digrafo com o número do componente fortemente conexo ao qual pertence Imprimir os vértices e os arcos de cada um desses componentes Usar o algoritmo de Kosaraju apresentado no Capítulo VII das aulas teóricas Poderão ser acrescentados novos campos às structs CelulaVertice e CelulaAdj