Algoritmos para Grafos, via Sedgewick  |  Índice remissivo

Estrutura de dados para redes de fluxo

Para representar redes capacitadas, poderíamos usar a estrutura de dados já empregada para digrafos com custos nos arcos, depois de substituir "custo" por "capacidade".   Mas é conveniente definir uma nova estrutura, sob medida para tratar do problema do fluxo máximo.  Em particular, é conveniente que a estrutura represente uma rede de fluxo, conforme a seguinte definição.

Uma rede de fluxo (= flow network) é uma

(Esta página é um resumo de parte da seção 22.1 (Flow Networks) do capítulo 22 (Network Flow) do livro de Sedgewick.)

Listas de adjacência

Nossas redes de fluxo serão representadas por listas de adjacência. Como de hábito, cada arco v-w será representado por um nó na lista adj[v].  Além do campo  w,  esse nó terá

O nó terá ainda um campo dup, cujo uso será descrito mais adiante.

/* Copiado do programa 22.1, p.366, de Sedgewick. */

typedef struct node *link;

struct node { 
   Vertex w; 
   int cap; 
   int flow; 
   link dup; 
   link next;
};

link NEW (Vertex w, int cap, int flow, link next) { 
   link x = malloc(sizeof *x);
   x->w = w; 
   x->cap = cap; 
   x->flow = flow; 
   x->next = next;     
   return x;                         
}

struct flownet { 
   int V, A;
   link *adj; 
   Vertex s, t; 
};

typedef struct flownet *Flownet;

Eis algumas funções óbvias para construção de uma rede de fluxo:

Flownet FLOWinit (int V) { 
   Vertex v;
   Flownet G = malloc(sizeof *G);
   G->adj = malloc(V * sizeof(link));
   G->V = V; 
   G->A = 0;
   for (v = 0; v < V; v++) G->adj[v] = NULL;
   return G;
}

/* Insere um arco v-w, de capacidade cap e fluxo nulo na rede G. */

void FLOWinsertA (Flownet G, Vertex v, Vertex w, int cap) { 
   if (v == w || cap < 0) return;
   G->adj[v] = NEW(w, cap, 0, G->adj[v]);
   G->adj[v]->dup = G->adj[w];
   G->A++;
}

Exercícios

  1. Escreva uma função que receba uma rede de fluxo e verifique se as capacidades dos arcos são mesmo não negativas e se os fluxos respeitam as capacidades. Em outras palavras, verifique se  0 ≤ flow ≤ cap  em cada arco.
  2. Escreva uma função que receba uma rede de fluxo e um vértice v e devolva o saldo do fluxo em v.  Escreva outra função que receba um rede de fluxo e verifique se os campos flow de fato representam um fluxo em relação aos vértices inicial s e final t.
  3. Escreva uma função que receba um arquivo contendo a descrição de uma rede capacitada e construa a correspondente estrutura flownet.  A primeira linha do arquivo deve especificar o número de vértices da rede e os seus vértices inicial e final; cada uma das linhas seguintes deve ter um arco e sua capacidade. Exemplo:
             6 0 5
             0-1   2
             0-2   3
             1-3   3
             1-4   1
             2-3   1
             2-4   1
             3-5   2
             4-5   3
    

    Preencha os campos flow dos arcos com zeros.

  4. Escreva uma função que gere uma rede capacitada aleatória com V vértices e A arcos. As capacidades devem ser escolhidas aleatoriamente no intervalo [0,M-1]. Os vértices inicial e final também devem ser escolhidos aleatoriamente.

Redes de fluxo expandidas

É difícil procurar (pseudo)caminhos de aumento numa rede de fluxo porque esses caminhos podem ter arcos inversos.  Para contornar essa dificuldade, vamos introduzir o conceito de rede de fluxo expandida. Nessa rede, será possível trabalhar exclusivamente com caminhos (dirigidos).

Suponha dada uma rede de fluxo. Para cada arco v-w, acrescente à rede um arco w-v.  Diremos que os novos arcos são artificiais e os antigos são originais  (cada arco artificial é antiparalelo ao correspondente arco original).   A capacidade de cada arco artificial w-v será, por definição, o negativo da capacidade do correspondente arco original v-w.  O fluxo em cada arco artificial será o negativo do fluxo no correspondente arco original. 

(É preciso admitir que a aplicação dos termos "capacidade" e "fluxo" aos arcos artificiais é abusiva, uma vez que esses números são tipicamente negativos.)

A rede que acabamos de construir será chamada rede de fluxo expandida, ou simplesmente rede expandida.  Ela representa, com certas vantagens, a rede de fluxo original.   [O conceito de rede expandida substitui, com vantagem, o conceito de "rede residual" de Sedgewick.]

(Se a rede original tiver arcos antiparalelos, a rede expandida terá arcos paralelos, o que entra em choque com nossa proibição de tais arcos.  Felizmente, a estrutura de dados que definimos acima é capaz de representar arcos paralelos. Podemos, portanto, desrespeitar a proibição ao tratar de redes expandidas.)

A estrutura de dados que definimos acima para representar redes de fluxo também é capaz de representar redes expandidas:  basta acrescentar os arcos artificiais à rede de fluxo original.  O campo  dup  nos nós das listas de adjacência será usado para apontar de um arco original para o correspondente arco artificial e vice-versa.  Para cada o arco artificial teremos

cap ≤ flow ≤ 0

e para cada o arco original teremos 0 ≤ flow ≤ cap.

Eis uma função que transforma uma rede de fluxo na correspondente rede de fluxo expandida:

/* Transforma uma rede de fluxo G na correspondente rede de fluxo expandida. */

void Expand (Flownet G) {
   Vertex s = G->s, t = G->t, v, w; link po, pa;
   int cap, flow;
   for (v = 0; v < G->V; v++) 
      for (po = G->adj[v]; po != NULL; po = po->next) 
         po->dup = NULL;
   for (v = 0; v < G->V; v++) 
      for (po = G->adj[v]; po != NULL; po = po->next) 
         if (po->dup == NULL) {
            w = po->w;
            cap = po->cap;
            flow = po->flow;
            G->adj[w] = pa = NEW(v, -cap, -flow, G->adj[w]);
            po->dup = pa;
            pa->dup = po;
         }
}

Veja como é fácil calcular o saldo do fluxo num vértice v de uma rede de fluxo expandida:

/* A função flowV calcula o saldo de fluxo no vértice v de uma rede de fluxo expandida G.  (Copiado do programa 22.2, p.367, de Sedgewick.) */

int flowV (Flownet G, Vertex v) { 
   link p; int x = 0;
   for (p = G->adj[v]; p != NULL; p = p->next)
      x += p->flow;
   return x;
}

A intensidade do fluxo nada mais é que o valor da expressão flowV(G, G->s).

Exemplo 1

Considere a rede de fluxo (não expandida) abaixo (esse exemplo é cópia de parte da figura 22.17, p.377, de Sedgewick).

         arco  cap flow    s = 0
         0-1   2      2    t = 5
         0-2   3      2
         1-3   3      1
         1-4   1      1
         2-3   1      1
         2-4   1      1
         3-5   2      2
         4-5   3      2

A tabela seguinte mostra a correspondente rede de fluxo expandida. Os arcos artificiais estão pintados de vermelho.

         arco  cap flow
         0-1   2      2
         1-0  -2     -2 
         0-2   3      2
         2-0  -3     -2 
         1-3   3      1
         3-1  -3     -1 
         1-4   1      1
         4-1  -1     -1 
         2-3   1      1
         3-2  -1     -1 
         2-4   1      1
         4-2  -1     -1 
         3-5   2      2
         5-3  -2     -2 
         4-5   3      2
         5-4  -3     -2 

Rede expandida e capacidades residuais

Um caminho (sem arcos inversos) de s a t na rede de fluxo expandida corresponde a um (pseudo)caminho de aumento na rede de fluxo original se os seus arcos originais têm fluxo menor que a capacidade e os seus arcos artificiais têm fluxo estritamente negativo, ou seja, se

para todo arco do caminho.   Diremos, simplesmente, que um caminho com essas propriedades é um caminho de aumento na rede de fluxo expandida.

Cada arco da rede de fluxo expandida tem uma capacidade residual, definida da seguinte maneira.  A capacidade residual de um arco original da rede expandida (ou seja, um arco com cap ≥ 0) é a diferença

cap - flow

e a capacidade residual de um arco artificial (ou seja, um arco com cap < 0) é

-flow .

(Note que essas capacidades residuais são números não negativos.)   O código de nossas implementações calculará as capacidades residuais dos arcos da rede de fluxo expandida sempre da mesma maneira:  Se  v  é um vértice e  L  é o endereço de um nó na lista  adj[v]  então o valor da expressão

           L->cap >= 0 ? L->cap - L->flow : -L->flow

é a capacidade residual do arco (original ou artificial) que tem ponta inicial v e ponta final L->w.

A capacidade residual de um caminho (dirigido) na rede de fluxo expandida é a menor das capacidades residuais de seus arcos.  (Observe como essa definição corresponde, precisamente, à definição de capacidade residual de um caminho de aumento na rede de fluxo original.)  Um caminho de s a t na rede de fluxo expandida é um caminho de aumento se e somente se sua capacidade residual é estritamente positiva.

Exemplo 2

Considere a rede de fluxo (não expandida) abaixo.  (Esse exemplo é cópia de parte da figura 22.17, p.377, de Sedgewick.)

         arco  cap  flow   s = 0
         0-1   2    2      t = 5
         0-2   3    1
         1-3   3    2
         1-4   1    0
         2-3   1    0
         2-4   1    1
         3-5   2    2
         4-5   3    1

A tabela seguinte mostra os arcos da correspondente rede de fluxo expandida com suas capacidades residuais.  Os arcos artificiais estão pintados de vermelho.

         arco  capres
         0-1   0
         1-0   2 
         0-2   2 
         2-0   1 
         1-3   1 
         3-1   2 
         1-4   1 
         4-1   0 
         2-3   1 
         3-2   0 
         2-4   0
         4-2   1 
         3-5   0
         5-3   2 
         4-5   2 
         5-4   1 

 


Veja também minha página Fluxo em Redes.
URL of this site: http://www.ime.usp.br/~pf/algoritmos_para_grafos/
Last modified: Mon Feb 20 10:36:29 BRST 2012
Paulo Feofiloff
IME-USP

Valid HTML 4.0!     Valid CSS!