17
分析:最大权闭合图问题,可以转化成最小割问题,进而用最大流解决。
【建模方法】
把每个实验看作二分图X集合中的顶点,每个设备看作二分图Y集合中的顶点,增加源S和汇T。
1、从S向每个Xi连接一条容量为该点收入的有向边。
2、从Yi向T连接一条容量为该点支出的有向边。
3、如果一个实验i需要设备j,连接一条从Xi到Yj容量为无穷大的有向边。
统计出所有实验的收入只和Total,求网络最大流Maxflow,最大收益就是Total-Maxflow。对应的解就是最小割划分出的S集合中的点,也就是最后能从S访问到的顶点的集合。
写完这题后我学到了如何解决最大权闭合子图的问题,还有一种新的求最小割的方法:从源点开始用一次fill,所有源点能到达的点即为最小割的S集合。
代码:
var n,m,x,tot,ans,i,s,t:longint; v:array[0..100] of boolean; d:array[0..100] of longint; c:array[0..100,0..100] of longint; function bfs:boolean; var head,tail,i,u:longint; state:array[1..100] of longint; begin head:=0; tail:=1; state[1]:=s; fillchar(d,sizeof(d),0); d[s]:=1; repeat inc(head); u:=state[head]; for i:=s to t do if (c[u,i]>0)and(d[i]=0) then begin d[i]:=d[u]+1; inc(tail); state[tail]:=i; if i=t then exit(true); end; until head>=tail; bfs:=false; end; function min(x,y:longint):longint; begin if x<y then exit(x) else exit(y); end; function dfs(x,maxf:longint):longint; var i,f,ret:longint; begin if x=t then exit(maxf); ret:=0; for i:=s to t do if (c[x,i]>0)and(d[i]=d[x]+1) then begin f:=dfs(i,min(maxf-ret,c[x,i])); dec(c[x,i],f); inc(c[i,x],f); ret:=ret+f; if maxf=ret then exit(ret); end; dfs:=ret; end; procedure fill(x:longint); var i:longint; begin v[x]:=false; for i:=s to t do if (c[x,i]>0)and(v[i]) then fill(i); end; begin // assign(input,'shut.in'); // assign(output,'shut.out'); reset(input); rewrite(output); readln(m,n); s:=0; t:=n+m+1; for i:=1 to m do begin read(x); c[s,i]:=x; tot:=tot+x; while not eoln do begin read(x); c[i,x+m]:=maxlongint div 3; end; readln; end; for i:=1 to n do begin read(x); c[m+i,t]:=x; end; while bfs do ans:=ans+dfs(s,maxlongint div 3); fillchar(v,sizeof(v),true); fill(s); for i:=1 to m do if not v[i] then write(i,' '); writeln; for i:=m+1 to n+m do if not v[i] then write(i-m,' '); writeln; writeln(tot-ans); close(input); close(output); end.