【网络流-最大流-Dinic-建模】POJ3281 Dining:Pascal 解法

传送门:POJ3281

【题目大意】

有N头奶牛,每一头都有自己喜欢的食物和饮料,且它们只吃自己喜欢的东西。每头牛只能选一个食物与一个饮料现在有F种食物,D种饮料,求最多能够同时满足几头奶牛的需求。

【输入格式】

第一行N,F,D。N代表牛的数量;F代表食物的数量;D代表饮料的数量

接下来2..N+1行:每行前两个数:F[I]与D[I]代表这头牛喜欢食物与饮料的种类数,接下来F+D行代表他喜欢的食物与饮料。

【输出格式】

一个数X:代表最多几头牛能满足。

【大致思路】

Dinic求最大流+邻接表

【建图思路】

最直白的方式是将食物放在左边,分别与源点相连(容量:1);饮料放在右边,分别与汇点相连(容量:1);每头奶牛看作一个点,放在中间。每头奶牛都与其喜爱的食物和饮料连一条边(容量:MAX),然后求这个图的最大流就行了。

但是,题目有限制每头奶牛只能选一份餐,所以要在这个基础上做限制:将表示牛的点一分为二(牛1和牛2),牛1决定所选的食物(与喜爱食物连边),牛2决定所选饮料(与喜爱饮料连边),两者之间再连一条容量为1的边即可。这个做法让每个牛只能选一种套餐。


【代码】

type
  dinic=record
    y,r,next,op:longint;
  end;

var
  g:array[-1..20000] of dinic;
  level,q,h:array[-1..410] of longint;
  n,m,i,ans,a,b,c,j,num,vs,vt,f,d,ff,dd,x:longint;

function min(a,b:longint):longint;
begin
 if a>b then exit(b) else exit(a);
end;

function dfs(v,a:longint):longint;
var
 ans,flow,tmp,u,value:longint;
begin
 if (v=vt) or (a=0) then exit(a);
 ans:=0;  tmp:=h[v];
 while tmp<>0 do
 begin
  u:=g[tmp].y;
  value:=g[tmp].r;
  if (level[u]=level[v]+1)  then
  begin
   flow:=dfs(u,min(a,value));
   if flow<>0 then
   begin
    g[tmp].r:=g[tmp].r-flow;
    g[g[tmp].op].r:=g[g[tmp].op].r+flow;
    ans:=ans+flow;
    a:=a-flow;
    if a=0 then break;
   end;
  end;
  tmp:=g[tmp].next;
 end;
 exit(ans);
end;

function bfs:boolean;
var
 i,f,r,tmp,v,u,j:longint;
begin
 fillchar(level,sizeof(level),0);
 f:=1;  r:=1;
 q[1]:=vs;
 level[vs]:=1;
 repeat
  v:=q[f];
  tmp:=h[v];
  while tmp<>0 do
  begin
   u:=g[tmp].y;
   if (g[tmp].r<>0) and (level[u]=0) then
   begin
    level[u]:=level[v]+1;
    inc(r);
    q[r]:=u;
    if u=vt then exit(true);
   end;
   tmp:=g[tmp].next;
  end;
 inc(f);
 until f>r;
 exit(false);
end;

procedure add(a,b,c:longint);
begin
 inc(num);
 g[num].y:=b;
 g[num].r:=c;
 g[num].op:=num+1;
 g[num].next:=h[a];
 h[a]:=num;
 inc(num);
 g[num].y:=a;
 g[num].r:=0;
 g[num].op:=num-1;
 g[num].next:=h[b];
 h[b]:=num;
end;

procedure init;
begin
 fillchar(h,sizeof(h),0);
 num:=0;
 ans:=0;
 readln(n,f,d);
 vs:=0;
 vt:=401;
 for i:=1 to f do add(vs,i+200,1);
 for i:=1 to d do add(i+300,vt,1);
 for i:=1 to n do
 begin
  add(i,i+100,1);
  read(ff,dd);
  for j:=1 to ff do
  begin
   read(x);
   add(x+200,i,maxlongint);
  end;
  for j:=1 to dd do
  begin
   read(x);
   add(i+100,x+300,maxlongint);
  end;
  readln;
 end;
end;

procedure main;
begin
  init;
  while bfs do
  begin
   ans:=ans+dfs(vs,maxlongint);
  end;
  writeln(ans);
end;

begin
 main;
end.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值