P3410 拍照

图片来自于 \(Hi\ Ho\)

简化一下题面 :

  • 一些人要拍照,拍照要一些 小 \(B\) 的下属一起拍,拍完了这些人会给小 \(B\) 钱。
  • 给了一个下属的钱就可以让他拍很多次照片。
  • 求小 \(B\) 的纯利润 (最大)。

很显然样例中可以全部人都拍,全部下属都给钱。 很显然这是一个最大权闭合子图问题。

先说一说什么是权闭合子图:

40625.png

这里有的权闭合子图有 \((3).(4).(2,4).(3,4).(1,3,4).(2,3,4).(1,2,3,4)\)。我们在一个图中可以选一些点,形成一个集合 \(V\),但是要保证这些点所连 (有向边) 到的点也要在 \(V\) 里面。所以很显然 \((1)\) 不是权闭合子图因为 \(3,4\) 没有进入。

很显然这种图可以用二分图展示:

40628.png

其中我们可以浅浅的理解,哪些点要哪些点。而如果我们建立了一个超汇的话,就可以知道 如果我们把让所有 \(B\)点连超汇,流量为所付的钱, \(A\)点连超源,流量为给的钱 ,那么我们就可以直接求 最大权闭合子图 了。因为我们流一条超源向 \(A\) 点的边就等于给他拍照拿他钱,而流 \(B\) 点向超汇的就是付给他钱 (而且仅仅一次,因为只流一次)。那么 \(A\)\(B\) 边的流量就可以设置为 \(inf\)

但是要知道结论: 最大权闭合子图的权值等于所有正权点之和减去最小割。(其中最小割等于最大流)

我们就跑一遍最大流就好啦。

(没想到连 _ 也可以当变量 \(qwq\))

// luogu-judger-enable-o2
Uses math;

Const _=100;

var
    value,reach,next:array[-1..50000] of longint;
    gap,dis,cnt:array[-1..50000] of longint;
    i,l,r,n,m,source,sink,sum,tot,ans:longint;
    maxflow:int64;

procedure add(l,r,sum:longint);
begin
    inc(tot);
    reach[tot]:=r;
    value[tot]:=sum;
    next[tot]:=cnt[l];
    cnt[l]:=tot;
end;

function Dfs(now,flow:longint):longint;
var i,k,mindis,ret:longint;
begin
    mindis:=n-1; ret:=flow;
    if now=sink then exit(flow);
    i:=cnt[now];
    while i<>-1 do
    begin
        if value[i]>0 then
        begin
            if dis[now]=dis[reach[i]]+1 then
            begin
                k:=Dfs(reach[i],min(ret,value[i]));
                dec(value[i],k); inc(value[i xor 1],k);
                dec(ret,k);
                if dis[source]>=n then exit(flow-ret);
                if ret=0 then break;
            end;
            mindis:=min(mindis,dis[reach[i]]);
        end;
        i:=next[i];
    end;
    if ret=flow then
    begin
        dec(gap[dis[now]]);
        if gap[dis[now]]=0 then dis[source]:=n;
        dis[now]:=mindis+1;
        inc(gap[dis[now]]);
    end;
    exit(flow-ret);
end;

begin
    filldword(cnt,sizeof(cnt) div 4,maxlongint*2+1); tot:=1;
    read(m,n); source:=0; sink:=n+m+1; 
    for i:=1 to m do
    begin
        read(l); inc(sum,l); add(source,i,l); add(i,source,0); read(l);
        while l<>0 do begin add(i,l+_,maxlongint div 843); add(l+_,i,0); read(l); end;
    end;
    for i:=1 to n do begin read(l); add(i+_,sink,l); add(sink,i+_,0); end;

    n:=n+m+_+1; gap[source]:=n;
    while dis[source]<n do inc(maxflow,Dfs(source,maxlongint div 842));

    writeln(sum-maxflow);
end.

转载于:https://www.cnblogs.com/FibonacciHeap/articles/9875900.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值