[4_4_milk6] Network flow

Pollutant Control
Hal Burch

It's your first day in Quality Control at Merry Milk Makers, and already there's been a catastrophe: a shipment of bad milk has been sent out. Unfortunately, you didn't discover this until the milk was already into your delivery system on its way to stores. You know which grocer that milk was destined for, but there may be multiple ways for the milk to get to that store.

The delivery system is made up of a several warehouses, with trucks running from warehouse to warehouse moving milk. While the milk will be found quickly, it is important that it does not make it to the grocer, so you must shut down enough trucks to ensure that it is impossible for the milk to get to the grocer in question. Every route costs a certain amount to shut down. Find the minimum amount that must be spent to ensure the milk does not reach its destination, along with a set of trucks to shut down that achieves this goal at that cost.

PROGRAM NAME: milk6

INPUT FORMAT

Line 1:Two space separated integers, N and M. N (2 <= N <= 32) is the number of warehouses that Merry Milk Makers has, and M (0 <= M <= 1000) is the number of trucks routes run. Warehouse 1 is actually the productional facility, while warehouse N is the grocer to which which the bad milk was destined.
Line 2..M+1:Truck routes: three space-separated integers, Si, Ei, and Ci. Si and Ei (1 <= Si,Ei <= N) correspond to the pickup warehouse and dropoff warehouse for the truck route. Ci (0 <= Ci <= 2,000,000) is the cost of shutting down the truck route.

SAMPLE INPUT (file milk6.in)

 
4 5
1 3 100
3 2 50
2 4 60
1 2 40
2 3 80

OUTPUT FORMAT

The first line of the output should be two integers, C and T. C is the minimum amount which must be spent in order to ensure the our milk never reaches its destination. T is the minimum number of truck routes that you plan to shut down in order to achive this goal. The next T lines sould contain a sorted list of the indexes of the truck routes that you suggest shutting down. If there are multiple sets of truck routes that achieve the goal at minimum cost, choose one that shuts down the minimum number of routes. If there are still multiple sets, choose the one whose initial routes have the smallest index.

SAMPLE OUTPUT (file milk6.out)

 
60 1
3













My solution: change edge capacity to consider the edge number in the cut
#include <algorithm>
#include <climits>
#include <fstream>

using namespace std;

const int NUM = 32, EDGENUM = 1000;
const long long FACTOR = EDGENUM + 1;

long long max_flow(const int n, const long long capacity[NUM][NUM]) {
        long long total_flow = 0, residual[NUM][NUM];
        for (int i = 0; i < n; ++i)
                for (int j = 0; j < n; ++j)
                        residual[i][j] = capacity[i][j];
        const int source = 0, target = n - 1;
        while (true) {
                long long path_flow, flow[NUM] = {0};
                flow[source] = LLONG_MAX;
                int pre[NUM], index;
                bool visit[NUM] = {0};
                while (true) {
                        path_flow = 0, index = -1;
                        for (int i = 0; i < n; ++i)
                                if (!visit[i] && path_flow < flow[i])
                                        path_flow = flow[i], index = i;
                        if (index == -1 || index == target) break;
                        visit[index] = true;
                        for (int i = 0; i < n; ++i)
                                if (!visit[i] && flow[i] < min(path_flow, residual[index][i]))
                                        flow[i] = min(path_flow, residual[index][i]), pre[i] = index;
                }
                if (index == -1) break;
                total_flow += path_flow;
                while (index != source) {
                        const int j = pre[index];
                        residual[j][index] -= path_flow;
                        residual[index][j] += path_flow;
                        index = j;
                }
        }
        return total_flow;
}

int main() {
        ifstream fin("milk6.in");
        int n, m;
        fin >> n >> m;
        long long graph[NUM][NUM] = {0};
        int s[EDGENUM], e[EDGENUM], c[EDGENUM];
        for (int i = 0; i < m; ++i) {
                fin >> s[i] >> e[i] >> c[i];
                --s[i], --e[i];
                graph[s[i]][e[i]] += c[i] * FACTOR + 1;
        }
        fin.close();

        long long ans = max_flow(n, graph), total = ans;
        int cnt = 0;
        bool flag[EDGENUM] = {false};
        for (int i = 0; i < m; ++i) {
                graph[s[i]][e[i]] -= c[i] * FACTOR + 1;
                long long tmp = max_flow(n, graph);
                if (ans - tmp == c[i] * FACTOR + 1) {
                        ++cnt;
                        flag[i] = true;
                        total -= (ans - tmp);
                }
                if (total == 0) break;
                graph[s[i]][e[i]] += c[i] * FACTOR + 1;
        }

        ofstream fout("milk6.out");
        fout << (ans / FACTOR) << ' ' << cnt << '\n';
        for (int i = 0; i < m; ++i)
                if (flag[i])
                        fout << (i + 1) << '\n';
        fout.close();

        return 0;
}

Official Analysis:
Pollutant Control
Hal Burch

This is a straight-forward min cut problem. Since we want the minimum set of edges in the case of a tie in total cost, multiply the cost of each edge by 1001 (one more than the maximum number of edges to delete) and add one. Thus, the cost of a min cut is the number of edges cut plus 1001 times the sum of the cost of the edges cut. Since the max cut can be very large, we must use doubles to avoid overflow.

Determine the max flow using the standard method. To determine a cut associated with this flow, look at the residual graph. Flood fill out from the start node. The boundary of the flood fill corresponds to one cut in the graph. To determine this cut, find all edges that go from a node visited by the flood fill to a node not visited by the flood fill.

This solution is from a Chinese student:

 
 
Program milk6;
var
 map,remap,crest:array[1..32,1..32] of longint;
 a:array[1..32,0..32] of longint;
 ans,cut,l1,l2,long:array[1..1000] of longint;
 from,point:array[1..32] of longint;
 flow,n,m,min,cn,s,dep:longint;
 procedure init;
 var
  i,j:longint;
 begin
 assign(input,'milk6.in');
 assign(output,'milk6.out');
 reset(input);
 rewrite(output);
 readln(n,m);
  for i:=1 to m do
  begin
  read(l1[i],l2[i],long[i]);
  inc(map[l1[i],l2[i]],long[i]);
  end;
  for i:=1 to n do
   for j:=1 to n do
    if map[i,j]>0
    then begin
         inc(a[i,0]);
         a[i,a[i,0]]:=j;
         inc(a[j,0]);
         a[j,a[j,0]]:=i;
         end;
 remap:=map;
 end;
 function find:boolean;
 var
  i,p1,p2:longint;
 begin
  fillchar(from,sizeof(from),255);
 p1:=1;
 p2:=1;
 point[1]:=1;from[1]:=0;
  repeat
   for i:=1 to a[point[p1],0] do
    if (from[a[point[p1],i]]<0) and (map[point[p1],a[point[p1],i]]>0)
    then begin
         inc(p2);
         point[p2]:=a[point[p1],i];
         from[point[p2]]:=point[p1];
          if point[p2]=n then exit(true);
         end;
  inc(p1);
  until p1>p2;
 exit(false);
 end;
 function maxflow:longint;
 var
  s,i,j:longint;
 begin
 s:=0;
  while true do
   if find
   then begin
        min:=999999;
        i:=n;
         while from[i]<>0 do
         begin
          if min>map[from[i],i]
          then min:=map[from[i],i];
          i:=from[i];
         end;
        s:=s+min;
        i:=n;
         while from[i]<>0 do
         begin
         dec(map[from[i],i],min);
         inc(map[i,from[i]],min);
         i:=from[i];
         end;
        end
   else break;
 exit(s);
 end;
 procedure mincut;
 var
  i:longint;
 begin
 s:=0;
 crest:=map;
  for i:=1 to m do
   if crest[l1[i],l2[i]]=0
   then begin
        map:=remap;
        dec(map[l1[i],l2[i]],long[i]);
         if flow-maxflow=long[i]
         then begin
              inc(cn);
              cut[cn]:=i;
              inc(s,long[i]);
              end;
        end;
 end;
 procedure print;
 var
  i,j:longint;
 begin
 writeln(flow,' ',dep);
  for i:=1 to dep do writeln(ans[i]);
 close(input);
 close(output);
 halt;
 end;
 procedure dfs(i,last,s:longint);
 var
  j:longint;
 begin
  if i>dep
  then begin
        if s=flow
        then print;
       exit;
       end;
  for j:=last+1 to cn do
  begin
  ans[i]:=j;
  dfs(i+1,j,s+long[j]);
  end;
 end;
 procedure main;
 var
  i,j:longint;
 begin
 flow:=maxflow;
 mincut;
  if s=flow
  then begin
       ans:=cut;
       dep:=cn;
       print;
       end;
  for dep:=1 to cn do dfs(1,0,0);
 end;
begin
init;
main;
end.

Zhang Yunxiao writes: Hal proposed a wonderful solution but it left two main points to desire: for one thing, Hal's augment function doesn't take full advantage of the array touched, so he flood-filled once more to find the min cut. For the other, Hal's output doesn't meet the condition set in the problem as Hal's output are the min cut set closest to the source, rather than "the one whose initial routes have the smallest index". My program improved the first aspect, but I didn't think out a nice solution for the second. I used the array mark (the corresponding array in Hal's solution is touched)to find the min cut. Then I split the first route in the min cut into two with the same source and end to check whether it can be replaced by routes with a smaller initial index. However, this solution won't work for multi-substitute cases, like the one below.

 
 
program milk6;
  const
    count_vertex=32;
    count_routes=1000;
  label
    chk_point;
  type
    rt=record
      s,e:byte;c:longint;
    end;
  var
    i,m,minr:integer;
    tflow,mflow:int64;
    n:byte;
    next:array[1..count_vertex]of byte;
    used:array[1..count_vertex]of array[1..count_vertex]of bytebool;
    sol,ps:array[0..count_routes]of integer;
    route:array[1..count_routes]of rt;
    network:array[1..count_vertex]of
      array[1..count_vertex]of int64;
  function max_flow:int64;
    var
      maxv,curv,nexv:byte;
      i,j:integer;
      maxflow,tflow:int64;
      mark:array[1..count_vertex]of bytebool;
      flow:array[1..count_vertex]of int64;
      prev:array[1..count_vertex]of integer;
      graph:array[1..count_vertex]of
      array[1..count_vertex]of int64;
    begin
      graph:=network;
      tflow:=0;
      fillchar(used,sizeof(used),0);
      while true do
        begin
          fillchar(mark,sizeof(mark),0);
          fillchar(prev,sizeof(prev),0);
          fillchar(flow,sizeof(flow),0);
          flow[1]:=mflow;
          while true do
            begin
              maxflow:=0;
              maxv:=0;
              for i:=1 to n do
                if (flow[i]>maxflow)and(not mark[i]) then
                  begin
                    maxv:=i;maxflow:=flow[i];
                  end;
              if (maxv=0)or(maxv=n) then break;
              mark[maxv]:=true;
              for i:=1 to n do
              if (flow[i]<maxflow)
                and(flow[i]<graph[maxv][i]) then
                begin
                  prev[i]:=maxv;
                  flow[i]:=graph[maxv][i];
                  if flow[i]>maxflow then flow[i]:=maxflow;
                end;
            end;
          if maxv=0 then break;
          maxflow:=flow[n];
          inc(tflow,maxflow);
          curv:=n;
          while curv<>1 do
            begin
              nexv:=prev[curv];
              inc(graph[curv][nexv],maxflow);
              dec(graph[nexv][curv],maxflow);
              curv:=nexv;
            end;
        end;
      for i:=1 to n do
        if mark[i] then
          for j:=1 to n do
            if (not mark[j])and(network[i][j]>0) then used[i][j]:=true;
      for i:=1 to m do
        if used[route[i].s][route[i].e] then
          begin
            inc(sol[0]);
            sol[sol[0]]:=i;
          end;
      max_flow:=tflow;
    end;
  begin
    assign(input,'milk6.in');
    reset(input);
    readln(n,m);
    for i:=1 to m do
      begin
        readln(route[i].s,route[i].e,route[i].c);
        inc(network[route[i].s][route[i].e],route[i].c*1002+1);
      end;
    close(input);
    minr:=0;
    mflow:=2000000000;
    mflow:=mflow*1002;
    inc(mflow);
    tflow:=max_flow;
    assign(output,'milk6.out');
    rewrite(output);
    minr:=tflow mod 1002;
    writeln(tflow div 1002,' ',minr);
{ I split the route into two with the same source and end to check whether this route is replaceable by routes with smaller initial number.}

chk_point:    if sol[1]>1 then
      begin
        ps:=sol;
        inc(m);route[m]:=route[sol[1]];
        route[m].c:=1;
        inc(network[route[sol[1]].s][route[sol[1]].e]);
        sol[0]:=0;
        tflow:=max_flow;
        if (sol[0]>ps[0])or(sol[1]>ps[1]) then
          begin
            dec(m);dec(network[route[sol[1]].s][route[sol[1]].e]);
            sol:=ps;
          end
        else goto chk_point;
      end;
    for i:=1 to minr do
      writeln(sol[i]);
    close(output);
  end.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值