这题一看就是最小割的网络流,于是乎转化为最大流。但是麻烦的是,同时还要处理最小的边数以及字典序最小的输出。这可怎么办啊?网上的各种办法都是把边权*1001+1等等。但是我仍然就得这种方法有待商榷。
在网上查了一下,得到了正解。我们先求出最小割,然后把边权从大到小排序。(这样的话,割的边数会更少)每次枚举一条边,模拟删掉这条边并再求最小割。如果最小割减少的量等于这条边的边权,说明这条边在最小割的集合里。然后真的删除这条边,继续处理。最后输出删除的边数即可。
最后提醒一下:从x到y可以有很多条不同的路,所以在删边时不能直接赋成0,而是减掉这条边的边权。
代码:
{ ID: ymwbegi1 PROG: milk6 LANG: PASCAL } var n,m,ans,tot,s,i:longint; c,c1:array[1..32,1..32] of longint; fa:array[1..32] of longint; v:array[1..1000] of boolean; side:array[0..1000,1..4] of longint; procedure init; var i,x,y,z:longint; begin readln(n,m); for i:=1 to m do begin readln(x,y,z); inc(c[x,y],z); inc(c1[x,y],z); side[i,1]:=x; side[i,2]:=y; side[i,3]:=z; side[i,4]:=i; end; end; function find(x:longint):boolean; var i:longint; begin if x=n then exit(true); for i:=1 to n do if (fa[i]=-1)and(c[x,i]>0) then begin fa[i]:=x; if find(i) then exit(true); end; find:=false; end; procedure add; var i,min:longint; begin min:=maxlongint; i:=n; while i>1 do begin if c[fa[i],i]<min then min:=c[fa[i],i]; i:=fa[i]; end; i:=n; ans:=ans+min; while i>1 do begin dec(c[fa[i],i],min); inc(c[i,fa[i]],min); i:=fa[i]; end; end; procedure main; var i:longint; begin for i:=2 to n do fa[i]:=-1; fa[1]:=0; while find(1) do begin add; for i:=2 to n do fa[i]:=-1; fa[1]:=0; end; end; procedure sort; var i,j:longint; begin for i:=1 to m-1 do for j:=i+1 to m do if (side[i,3]<side[j,3])or(side[i,3]=side[j,3])and(side[i,4]>side[j,4]) then begin side[0]:=side[i];side[i]:=side[j];side[j]:=side[0]; end; end; begin assign(input,'milk6.in'); assign(output,'milk6.out'); reset(input); rewrite(output); init; main; write(ans,' '); sort; tot:=ans; fillchar(v,sizeof(v),false); for i:=1 to m do begin ans:=0; c:=c1; dec(c[side[i,1],side[i,2]],side[i,3]); main; if tot-ans=side[i,3] then begin inc(s); v[side[i,4]]:=true; dec(c1[side[i,1],side[i,2]],side[i,3]); tot:=ans; end else inc(c[side[i,1],side[i,2]],side[i,3]); end; writeln(s); s:=0; for i:=1 to m do if v[i] then writeln(i); close(input); close(output); end.