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
#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:
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.