Ordenação topológica - Kahn
Algoritmo de Kahn de ordenação topológica para resolver a questão ORKUT da OBI 2004.
Slides sobre ordenação topológica: https://docs.google.com/presentation/d/190PjxCX75m6VpJrNdR02j4yZJJPTGXl3OxQnrL_ON08/edit?usp=sharing
Link para a questão: https://www.thehuxley.com/problem/603
Código:
/*
Rodrigo Paes
Exemplo do algoritmo de KAHN para ordenação topológica.
Questão ORKUT - OBI 2004
https://www.thehuxley.com/problem/603
*/
#include <bits/stdc++.h>
using namespace std;
typedef vector<int> vi;
typedef vector<vi> vvi;
// Mapa do nome da pessoa para o vértice
unordered_map<string, int> indexes;
// Vértice para o nome
vector<string> reverse_index;
// Lista de adjancências
vvi adj_list;
// Conta quantas arestas chegam no vértice i
vi indegree;
// Verifica se o nó foi removido do grafo (remoção lógica)
vector<bool> removed;
// Resposta da ordenação topológica
vi top_order;
/*
Retorna verdadeiro caso tenha conseguido encontrar uma ordenação topológica.
Nesse caso, a resposta está em top_order
*/
bool top_sort()
{
queue<int> q;
int u;
/*
Adiciona na fila todos os vértices que não possuem
nenhuma aresta chegando nele.
*/
for (int i=0; i< adj_list.size(); ++i)
{
if (indegree[i] == 0 )
{
q.push(i);
}
}
while (!q.empty())
{
u = q.front(); q.pop();
/*
Se esse nó estava na fila, então nenhuma aresta chegava nele.
O que significa que ele não depende mais de ninguém e, portanto,
é parte da ordenação.
*/
top_order.push_back(u);
/* Marca o nó como removido do grafo */
removed[u] = true;
/*
Se o nó foi removido, para cada nó adjacente precisamos
diminuir 1 do contador de arestas que chegam nesse nó adjacente, afinal,
o nó não existe mais.
*/
for (int k=0; k<adj_list[u].size(); ++k)
{
int v = adj_list[u][k];
/*
Caso o nó adjancente não tenha sido removido, então subtraímos 1 do contador
de arestas. Caso o nó adjacente fique com 0 arestas, significa que ele agora
será um candidato a ser colocado na fila, e o processo se repetirá.
*/
if (!removed[v] && --indegree[v] == 0)
{
q.push(v);
}
}
}
/* Se ao final do processo, todos os nós foram adicionados na em top_order,
então significa que todos os elementos foram para a fila, o que significa
que todas as pendências anteriores a ele foram satisfeitas e, portanto,
encontramos uma ordem.
*/
return adj_list.size() == top_order.size();
}
void clear_all(int n)
{
indexes.clear();
reverse_index.clear();
adj_list.clear();
adj_list.resize(n);
indegree.clear();
indegree.resize(n,0);
removed.clear();
removed.resize(n, false);
top_order.clear();
}
int main()
{
int n;
string name;
string source, target;
int k, u, v;
int test_case = 1;
cin >> n;
while (n)
{
clear_all(n);
for (int i=0; i<n; ++i)
{
cin >> name;
indexes[name] = i;
reverse_index.push_back(name);
}
for (int i=0; i<n; ++i)
{
cin >> target >> k;
v = indexes[target];
for (int j=0; j< k; ++j)
{
cin >> source;
u = indexes[source];
indegree[v]++;
adj_list[u].push_back(v);
}
}
cout << "Teste "<< test_case++ << endl;
if (top_sort())
{
for (int i=0; i<top_order.size(); ++i)
{
if (i!=0)
{
printf(" ");
}
cout << reverse_index[top_order[i]];
}
printf("\n");
}
else
{
printf("impossivel\n");
}
cin >> n;
if (n) cout << endl;
}
return 0;
}