SGU 242(MaxFlow)

探讨如何利用最大流算法解决NN个学生选择KK所学校的问题,确保每所学校至少被两个学生选择。通过构建图模型并运用Dinic算法实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意简述

N个学生,K所学校(0N,K200),给定每个学生喜爱的若干所学校。每个学生仅可以选择一所他喜爱的学校(也可不选),问是否存在一种选择方案使每所学校至少被2个学生选择,若存在,请打印任意一种方案。


建 图

1.建立源点S,汇点T
2.从源点S向每个学生连一条容量为 1 的边。(限制每个学生的选择)
3.从每所学校向汇点T连一条容量为 2 的边。(满流即可)
4.从每个学生向其喜爱的学校连一条容量为 1 的边。


算法实现

直接跑Dinic最大流算法
若最大流为2K,则存在一种选择方案
输出方案:若一条从学生流向学校的边容量为0,则该边被选择,即该学生选择了该校


Hint

如果你RE或WA了很久,可以向我要SPJ和随机数据生成器。
蠢蠢的博主因为反向边表示错误,调了很久……


type
  edge=record
  re,next,cap:longint;
end;
var
i,j:longint;
cnt,n,k,s,t,ans,xc,x,tmp,zhi:int64;
e:array[0..100000] of edge;
f,d,q:array[0..600] of longint; 
vs:array[0..600] of boolean;
a:array[0..600,0..2] of longint;
boo:array[0..600] of boolean;

procedure add(x,y,tmp:longint);
begin
  inc(cnt);
  with e[cnt] do
  begin
    re:=y;
    next:=f[x];
    cap:=tmp;
  end;
  f[x]:=cnt;
end;

procedure bfs;
var
head,tail,x,tmp:longint;
begin
  fillchar(vs,sizeof(vs),0);
  fillchar(d,sizeof(d),0);
  head:=1;tail:=1;q[1]:=s;vs[s]:=true;
  while head<=tail do
  begin
    x:=q[head];tmp:=f[x];
    while tmp<>0 do
    begin
      with e[tmp] do
      begin
        if (cap<>0)and not vs[re] then
        begin
          inc(tail);q[tail]:=re;
          vs[re]:=true;d[re]:=d[x]+1;
        end;
      end;
      tmp:=e[tmp].next;
    end;
    inc(head);
  end;
end;

function min(x,y:int64):int64;
begin
  if x<y then min:=x else min:=y;
end;

function dfs(x,rec:longint):longint;
var
ret,dd,tmp:longint;
begin
  ret:=0;
  if x=t then begin dfs:=rec;exit;end;
  tmp:=f[x];
  while (tmp<>0)and(rec<>0) do
  begin
    if (d[e[tmp].re]=d[x]+1)and(e[tmp].cap<>0) then
    begin
      dd:=dfs(e[tmp].re,min(e[tmp].cap,rec));
      if dd<>0 then
      begin
        dec(rec,dd);inc(ret,dd);
        dec(e[tmp].cap,dd);inc(e[(tmp-1) xor 1 +1].cap,dd);
        if rec=0 then begin dfs:=ret; exit; end;
      end;
    end;
    tmp:=e[tmp].next;
  end;
  dfs:=ret;
  if ret=0 then d[x]:=-1;
end;

procedure dinic;
begin
  while true do
  begin
    bfs;
    if not vs[t] then exit;
    inc(ans,dfs(s,5000000));
  end;
end;

begin
  readln(n,k);
  s:=500;t:=501;cnt:=0;
  for i:=1 to n do
  begin
    read(xc);
    add(s,i,1);add(i,s,0);
    for j:=1 to xc do
    begin
      read(x);add(i,x+n,1);add(x+n,i,0);
    end;
  end;
  for i:=1 to k do
  begin add(i+n,t,2);add(t,n+i,0);end;

  ans:=0;
  dinic;

  if ans<>2*k then writeln('NO')
  else
  begin
    for i:=1 to n do
    begin
      tmp:=f[i];
      while tmp<>0 do
      begin
        with e[tmp] do
        if (cap=0)and(re<>s) then
        begin
          inc(a[re-n,0]);a[re-n,a[re-n,0]]:=i;
          break;
        end;
        tmp:=e[tmp].next;
      end;
    end;
    writeln('YES');
    for i:=1 to k do writeln('2 ',a[i,1],' ',a[i,2]);
  end;
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值