読者です 読者をやめる 読者になる 読者になる

*iroi*

mechairoi の Blog

Maximum flow (Dinic)

perl

ゼミでやったので, 今更だけど最大フローを実装してみる.
O(n^2m)になってるはずだけど長い..

#!/usr/bin/env perl
use strict;
use warnings;
#use Carp::Assert;
use Data::Dumper;

package Edge;
sub new {
  my ($class, $source, $target, $weight) = @_;
  my $self = {
    source => int($source),
    target => int($target),
    weight => $weight,
  };
  return bless $self, $class;
}

package Vertex;
sub new {
  my ($class, $id) = @_;
  my $self = {
    id    => int($id),
    edges => [],
  };
  return bless $self, $class;
}

package main;
use List::Util;

my $INF = 1e+20;
sub augment { # O(nm)
  my ($levelGraph, $flow, $source, $tink) = @_;
  my $dfs;
  $dfs = sub { # return new flow
    my ($v, $minCap) = @_;
    return $minCap if ($v->{id} == $tink);
    while (@{$v->{edges}}) {
      my $newflow = $dfs->($levelGraph->[$v->{edges}[0]{target}],
        List::Util::min ($minCap, $v->{edges}[0]{weight}));
      if ($newflow) {
        my $e = $v->{edges}[0];
        $flow->[$e->{source}][$e->{target}] += $newflow;
        $e->{weight} -= $newflow;
        shift @{$v->{edges}} unless ($e->{weight});
        return $newflow;
      } else {
        shift @{$v->{edges}};
      }
    }
    return 0;
  };
  my $ret;
  $ret += $dfs->($levelGraph->[$source], $INF) while(@{$levelGraph->[$source]{edges}});
  #assert($ret) if DEBUG;
  return $ret;
}

sub makeLevelGraph { # O(n^2)
  my ($flow, $N, $capacity, $source, $tink) = @_;
  my (@level, @levelGraph, @queue);
  $level[$_] = 0 for(0..$N-1);
  $levelGraph[$_] = new Vertex($_) for(0..$N-1);
  push @queue, $levelGraph[$source];
  $level[$source] = 1;
  while(@queue) { # bfs
    my $v = shift @queue;
    return \@levelGraph if ($v->{id} == $tink);
    for(my $i=0; $i<$N; $i++) {
      my $residualCapacity = $capacity->[$v->{id}][$i]
        - List::Util::max($flow->[$v->{id}][$i] - $flow->[$i][$v->{id}], 0);
      if(($level[$i] == 0 or $level[$i] == $level[$v->{id}]+1)
          and $residualCapacity > 0) {
        push @{$v->{edges}}, new Edge($v->{id}, $i, $residualCapacity);
        if($level[$i] == 0) {
          $level[$i] = $level[$v->{id}]+1;
          push @queue, $levelGraph[$i];
        }
      }
    }
  }
  return 0;
}

sub maxFlow {
  my ($N, $edges, $source, $tink) = @_;
  my ($capacity, $flow); #adjacent matrix
  my $totalFlow;
  for(my $i=0;$i<$N;$i++) {
    for(my $j=0;$j<$N;$j++) {
      $flow->[$i][$j]=0;
      $capacity->[$i][$j]=0;
    }
  }

  foreach my $e (@$edges) {
    $capacity->[$e->{source}][$e->{target}]+=$e->{weight};
  }
  while(my $levelGraph=makeLevelGraph($flow, $N, $capacity, $source, $tink)) {
    #at most n times
    $totalFlow+=augment($levelGraph, $flow, $source, $tink);
  }
  return $totalFlow;
}