题目大意:有N种药品,每种药品由若干种药材制成,有一个权值Pi,所有药材也恰好为N种,且保证有一种方案使每种药品一 一对应对应一个其使用的药材(即有完美匹配)。求一种选药材的方案,使选的药品数和它们使用的药材数相等,且最小化选出的药品权值和。(1<=N<=300, |Pi|<=1e7)
解法一:建立最小割模型,源点向所有药品连边,容量为INF-pi,药品向制作它的药材连边容量为IIINF(比INF大一级别的),所有药材向汇点连边,容量为INF。求出最小割,再减去源点连出的所有边的容量和即为答案。
为什么呢?首先IIINF保证了药品和药材的边不会被割。我们考虑从所有药品中删去一些药品选剩下的药品,所以要尽可能删掉权值大的那些,即割掉汇点与那些药品连的边,因为是最小割,所以先要对pi先取反。为什么要加INF呢,考虑一个药品没被割,那它对应的那些药材必须全部要割掉,如果割掉的药材比保留的药品多,那么总的最小割含的INF个数就会大于N,然而多一个INF都是致命的,所以这样保证了选的药品数和它们使用的药材数相等。最终计算答案要用所有的INF-pi减去最小割,再取反,所以直接用最小割减去所有INf-pi即可。
解法二:先考虑把药材和药品建成二分图,跑一个最大匹配,那如果要选一个药品,它所对应的 所有药材 所匹配的药品 都必须选,于是我们新建一个图,如果一个药品要选导致其他的一些药品也要选,就从这个点向那些点连边,跑一边最大权闭合子图即可。
解法一代码:
type
edge=^edgenode;
edgenode=record
t,c,f:int64;
rev,next:edge;
end;
var
n,i,j,x,size:longint;
ans:int64;
s:array[1..310]of int64;
p,t:array[1..310]of longint;
e:array[1..310,1..310]of longint;
con:array[0..2000]of edge;
ne,dl:array[0..2000]of longint;
visit:array[0..2000]of boolean;
const inf=1000000000;
iinf=1000000000000000000;
procedure ins(x,y,c,f:int64);
var
p:edge;
begin
new(p);
p^.t:=y;
p^.c:=c;
p^.f:=0;
p^.next:=con[x];
con[x]:=p;
new(p);
p^.t:=x;
p^.c:=0;
p^.f:=0;
p^.next:=con[y];
con[y]:=p;
con[x]^.rev:=con[y];
con[y]^.rev:=con[x];
end;
function min(x,y:int64):int64;
begin
if x>y then exit(y)
else exit(x);
end;
function bfs:boolean;
var
p:edge;
head,tail:longint;
begin
head:=1;
tail:=1;
for i:=0 to size do
begin
visit[i]:=false;
ne[i]:=0;
end;
visit[0]:=true;
dl[1]:=0;
bfs:=false;
while head<=tail do
begin
if dl[head]=size then bfs:=true;
p:=con[dl[head]];
while p<>nil do
begin
if (p^.f<p^.c)and(visit[p^.t]=false) then
begin
ne[p^.t]:=ne[dl[head]]+1;
visit[p^.t]:=true;
inc(tail);
dl[tail]:=p^.t;
end;
p:=p^.next;
end;
dl[head]:=0;
inc(head);
end;
end;
function dinic(k:longint;z:int64):int64;
var
p:edge;
o:int64;
begin
if k=size then exit(z);
if visit[k]=true then exit(0);
p:=con[k];
dinic:=0;
while p<>nil do
begin
if (ne[p^.t]=ne[k]+1)and(p^.f<p^.c) then
begin
o:=dinic(p^.t,min(z-dinic,p^.c-p^.f));
dinic:=dinic+o;
inc(p^.f,o);
if p^.rev<>nil then dec(p^.rev^.f,o);
if dinic=z then break;
end;
p:=p^.next;
end;
if dinic=0 then visit[k]:=true;
end;
procedure add;
var
p:edge;
begin
p:=con[0];
while p<>nil do
begin
ans:=ans-p^.c;
p:=p^.next;
end;
end;
begin
readln(n);
for i:=1 to n do
begin
read(t[i]);
for j:=1 to t[i] do
read(e[i,j]);
end;
for i:=1 to n do
read(p[i]);
for i:=1 to n do
for j:=1 to t[i] do
ins(i,e[i,j]+n,iinf,0);
size:=2*n+1;
for i:=1 to n do
begin
ins(0,i,inf-p[i],0);
ins(i+n,size,inf,0);
end;
ans:=0;
while bfs do
begin
for i:=0 to size do
visit[i]:=false;
ans:=ans+dinic(0,iinf);
end;
add;
writeln(ans);
end.