您的位置:首页 > 其它

pku2987 最小割,最大权闭合图

2010-05-16 20:00 495 查看
好吧, 我一开始认为我网络流还可以的,毕竟ctsc拿到题拿到了全场最高分(30分,集训队有10人左右拿到)。今天我发现我不论什么都很菜的,比如说上午的初赛吧。。额,,这个一会说,反正刚才这个题我就菜了,连割边都不会求的菜,终于知道割边原来用dfs。。

题目大意:

给你一个图,上边有n个点,每个点有一些关系A,B表示A是B的上司,现在要你裁人,如果裁了一个人就要裁他的下属。好了,每个人都有一定的权值,让你求两个问题:

1.裁完人的最大权值

2.满足第一个条件的最少人数

(其实这两问输出时倒过来的)

简略题解:

恩,网络流。

首先,添加源点st和汇点ed。对于w[i]>0的从源点向这个点连一条边,流量为w[i],否则从这个点向汇点连一条边,流量为-w[i]。然后对于有关系的A和B,从A向B连一条边,流量为无穷。

求这个图的最大流,最大的流量就是最小割,然后所有w[i]>0的sum-最小割就是第一问的答案。

然后从源点开始dfs,能够达到的点的个数就是第二问的答案。

附程序:

program pku_2987;
const inf=100000000000000;
var pre,other:array[0..150000] of longint;
c:array[0..150000] of int64;
v:array[0..5002] of boolean;
last,d,num,w:array[0..5002] of longint;
ans,sum,flow:int64;
n,m,st,ed,l:longint;
{-------------------------}
procedure grow(x,y:longint;z:int64);
begin
inc(l);
pre[l]:=last[x];last[x]:=l;other[l]:=y;c[l]:=z;
inc(l);
pre[l]:=last[y];last[y]:=l;other[l]:=x;c[l]:=0;
end;
{-------------------------}
procedure init;
var i,p,q:longint;
begin
readln(n,m);
st:=n+1;ed:=st+1;
for i:=1 to n do
begin
readln(w[i]);
if w[i]>0 then begin
grow(st,i,w[i]);
inc(sum,w[i]);
end
else grow(i,ed,-w[i]);
end;
for i:=1 to m do
begin
readln(p,q);
grow(p,q,inf);
end;
end;
{-------------------------}
function min(x,y:int64):int64;
begin
if x<y then exit(x);
exit(y);
end;
{-------------------------}
function sap(u:longint;flow:int64):int64;
var i,t:longint;
begin
if u=ed then exit(flow);
sap:=0;
i:=last[u];
while i<>0 do begin
if (c[i]>0) and (d[u]=d[other[i]]+1) then begin
t:=sap(other[i],min(flow-sap,c[i]));
inc(sap,t);
dec(c[i],t);
if odd(i) then inc(c[i+1],t) else inc(c[i-1],t);
if sap=flow then exit(sap);
end;
i:=pre[i];
end;
if d[st]>=ed then exit;
dec(num[d[u]]);
if num[d[u]]=0 then d[st]:=ed;
inc(d[u]);
inc(num[d[u]]);
end;
{-------------------------}
procedure dfs(u:longint);
var i:longint;
begin
if (u<>st) and (u<>ed) then inc(ans);
v[u]:=true;
i:=last[u];
while i<>0 do begin
if (c[i]>0) and not v[other[i]] then dfs(other[i]);
i:=pre[i];
end;
end;
{-------------------------}
procedure main;
begin
num[0]:=ed;
while d[st]<ed do
begin
flow:=sap(st,inf);
inc(ans,flow);
end;
flow:=sum-ans;
ans:=0;
dfs(st);
writeln(ans,' ',flow);
end;
{-------------------------}
begin
init;
main;
end.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: