学到很多知识的一道题目
一开始读错题,后来发现是每条边必须至少访问一次
明显是一个有下界的最小流
首先是我自己脑补的比较渣的算法,可以无视:
对于有下界的最小流,我不会做,但是我会做有下界的费用流,而且注意这是一个DAG图
只要对每条边建x-->y (flow=1 cost=-inf)和x-->y (flow=inf cost=0)这样两条边
(这是解决DAG图的有下界费用流的非常简便的做法,通过附加边,根据最短路原理一定能保证这条边下界流量被流过)
然后从源点到每个出度非0的点连一条cost=1 flow=inf的边,出度为0的点到汇点连边flow=inf cost=0
然后跑最小费用流(注意这里并不是最大流,只要增广到正费用,就代表原图的边都被走过了,可以直接退出)
最后再加上m*inf即是ans
但是很可惜,这个方法虽然容易想,但是跑得慢(1100ms) 而最优的解法是8ms,差距啊……
本着精益求精的精神,下面介绍有下界的网络流
首先这是一个有源有汇的有下界最小流,要想解决这个问题,就先要解决无源无汇有下界的可行流(循环流)
无源无汇是什么意思呢,那就表示每个点的出流=入流
令每条边上界为a,下界为b,则这样
我们考虑将有下界转化为下界为0的网络流,这样原来每条边的上界变成a-b
但经过这样更改流量是不守恒的,因此,我们添加源汇为s,t
令d(u)=该点入下界流和-该点出下界流和
如果d(u)>0 则连边s-->u flow=d(u)
如果d(u)<0 则连边u-->t flow=-d(u)
不难发现,如果要流量满足下界,那必然s延伸出的附加边一定是满流,这是有附加边流量的意义决定的
因此我们只要做s到t的最大流,然后判断s延伸出的附加边是否满流
如果满流,则存在循环流,如果不存在,那就无解
有了这个基础,我们就不难解决有源有汇的有上下界最大流的问题
首先先转化为已知问题,添加t-->s下界为0,上界为inf的边
这样就转化为无源无汇有下界的可行流,先按上述方法添加超级源汇ss,tt
然后判断是否存在可行流,存在则记为f1
然后再跑s-->t的最大流(这里是改造后的图,即每条边下界为0,上界为a-b)记为f2,
UPD:注意,这里最大流不是f1+f2而是单独的f2,为什么呢?
其实我们在跑完可行流之后,有些边的反向弧存了可行流的流量,这些反向弧可以构成s-->t流量为可行流的路径
所以f2实际上是包含可行流的,具体画一个图就明白了
容易出问题的是有源有汇的有上下界最小流
一开始很容易误认为,添加t-->s下界为0,上界为inf的边,做出来的可行流不就是最小流吗?
实际上是不对的,具体见http://www.cnblogs.com/kane0526/archive/2013/04/05/3001108.html
(注意那张图1--tt之间的边方向似乎反了)
可以发现,当出现循环流的时候,可行流会增大。
因此正确的做法是先不添加t-->s,直接按无源无汇有下界可行流做
然后再添加t-->s,然后再做ss到tt的最大流,如果附加边满流即有解,解就是t-->s实际流过的流量
这应该怎么理解呢?其实第一次做最大流就是在消去循环会带来增加的流量
好再回到这道题目上来,由于这道题目一定有解,而且源点,汇点所连的边下界为0
我们可以稍稍变动一下算法,具体见程序

 const inf=;
type node=record
next,point,flow,cost:longint;
end; var edge:array[..] of node;
q:array[..] of longint;
p,d,pre,cur:array[..] of longint;
v:array[..] of boolean;
s,i,len,n,t,j,x,m:longint; function min(a,b:longint):longint;
begin
if a>b then exit(b) else exit(a);
end; procedure add(x,y,f,c:longint);
begin
inc(len);
edge[len].point:=y;
edge[len].flow:=f;
edge[len].cost:=c;
edge[len].next:=p[x];
p[x]:=len;
end; function spfa:boolean;
var y,i,f,r,x:longint;
begin
d[]:=;
for i:= to t do
d[i]:=inf;
f:=;
r:=;
q[]:=;
fillchar(v,sizeof(v),false);
v[]:=true;
while f<=r do
begin
x:=q[f];
v[x]:=false;
i:=p[x];
while i<>- do
begin
y:=edge[i].point;
if edge[i].flow> then
if d[y]>d[x]+edge[i].cost then
begin
d[y]:=d[x]+edge[i].cost;
pre[y]:=x;
cur[y]:=i;
if not v[y] then
begin
inc(r);
q[r]:=y;
v[y]:=true;
end;
end;
i:=edge[i].next;
end;
inc(f);
end;
if d[t]=inf then exit(false) else exit(true);
end; function mincost:longint;
var i,j:longint;
begin
mincost:=;
while spfa do
begin
if d[t]> then exit; //关键
i:=t;
while i<> do
begin
j:=cur[i];
dec(edge[j].flow);
inc(edge[j xor ].flow);
i:=pre[i];
end;
mincost:=mincost+d[t];
end;
end; begin
len:=-;
fillchar(p,sizeof(p),);
readln(n);
t:=n+;
for i:= to n do
begin
read(s);
m:=m+s;
for j:= to s do
begin
read(x);
add(i,x,,-inf);
add(x,i,,inf);
add(i,x,inf,);
add(x,i,,);
end;
if s= then
begin
add(i,t,inf,);
add(t,i,,);
end
else begin
add(,i,inf,);
add(i,,,-);
end;
end;
writeln(mincost+m*inf);
end.

脑补的做法

 const inf=;
type node=record
next,point,flow:longint;
end; var edge:array[..] of node;
p,d,pre,cur,numh,h,c:array[..] of longint;
v:array[..] of boolean;
s,i,len,n,t,j,x,m:longint; function min(a,b:longint):longint;
begin
if a>b then exit(b) else exit(a);
end; procedure add(x,y,f:longint);
begin
inc(len);
edge[len].point:=y;
edge[len].flow:=f;
edge[len].next:=p[x];
p[x]:=len;
end; function sap:longint;
var i,q,tmp,u,j,neck:longint;
begin
neck:=inf;
for i:= to t do
cur[i]:=p[i];
numh[]:=t+;
u:=;
sap:=;
while h[]<t+ do
begin
d[u]:=neck;
i:=cur[u];
while i<>- do
begin
j:=edge[i].point;
if (edge[i].flow>) and (h[u]=h[j]+) then
begin
pre[j]:=u;
cur[u]:=i;
neck:=min(neck,edge[i].flow);
u:=j;
if u=t then
begin
sap:=sap+neck;
while u<> do
begin
u:=pre[u];
j:=cur[u];
dec(edge[j].flow,neck);
inc(edge[j xor ].flow,neck);
end;
neck:=inf;
end;
break;
end;
i:=edge[i].next;
end;
if i=- then
begin
dec(numh[h[u]]);
if numh[h[u]]= then exit;
tmp:=t;
q:=-;
i:=p[u];
while i<>- do
begin
j:=edge[i].point;
if edge[i].flow> then
if h[j]<tmp then
begin
tmp:=h[j];
q:=i;
end;
i:=edge[i].next;
end;
cur[u]:=q;
h[u]:=tmp+;
inc(numh[h[u]]);
if u<> then
begin
u:=pre[u];
neck:=d[u];
end;
end;
end;
end; begin
len:=-;
fillchar(p,sizeof(p),);
readln(n);
t:=n+;
for i:= to n do
begin
read(s);
for j:= to s do
begin
read(x);
add(i,x,inf);
add(x,i,);
inc(c[x]);
dec(c[i]);
end;
end;
for i:= to n do
if c[i]> then
begin
m:=m+c[i];
add(,i,c[i]);
add(i,,);
end
else if c[i]< then
begin
add(i,t,-c[i]);
add(t,i,);
end; writeln(m-sap); //一定有解的时候只用跑一次,想想看为什么
end.

标算

bzoj2502的更多相关文章

  1. [BZOJ2502]清理雪道

    [BZOJ2502]清理雪道 试题描述 滑雪场坐落在FJ省西北部的若干座山上. 从空中鸟瞰,滑雪场可以看作一个有向无环图,每条弧代表一个斜坡(即雪道),弧的方向代表斜坡下降的方向. 你的团队负责每周定 ...

  2. 【BZOJ2502】清理雪道 有上下界的网络流 最小流

    [BZOJ2502]清理雪道 Description        滑雪场坐落在FJ省西北部的若干座山上. 从空中鸟瞰,滑雪场可以看作一个有向无环图,每条弧代表一个斜坡(即雪道),弧的方向代表斜坡下降 ...

  3. 【BZOJ-2502】清理雪道 有上下界的网络流(有下界的最小流)

    2502: 清理雪道 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 594  Solved: 318[Submit][Status][Discuss] ...

  4. BZOJ2502:清理雪道(有上下界最小流)

    Description        滑雪场坐落在FJ省西北部的若干座山上. 从空中鸟瞰,滑雪场可以看作一个有向无环图,每条弧代表一个斜坡(即雪道),弧的方向代表斜坡下降的方向. 你的团队负责每周定时 ...

  5. [BZOJ2502]清理雪道 有上下界网络流(最小流)

    2502: 清理雪道 Time Limit: 10 Sec  Memory Limit: 128 MB Description        滑雪场坐落在FJ省西北部的若干座山上. 从空中鸟瞰,滑雪场 ...

  6. 【bzoj2502】清理雪道 有上下界最小流

    题目描述 滑雪场坐落在FJ省西北部的若干座山上. 从空中鸟瞰,滑雪场可以看作一个有向无环图,每条弧代表一个斜坡(即雪道),弧的方向代表斜坡下降的方向. 你的团队负责每周定时清理雪道.你们拥有一架直升飞 ...

  7. [BZOJ2502]清理雪道解题报告|带下界的最小流

    滑雪场坐落在FJ省西北部的若干座山上. 从空中鸟瞰,滑雪场可以看作一个有向无环图,每条弧代表一个斜坡(即雪道),弧的方向代表斜坡下降的方向. 你的团队负责每周定时清理雪道.你们拥有一架直升飞机,每次飞 ...

  8. bzoj2502: 清理雪道(有源汇有上下界最小流)

    传送门 别说话,自己看,我不会->这里 我这里用的建图方法是先跑一次最大流,连上$(t,s,inf)$之后再跑一遍,然后答案就是之前连的那条边的反向边的流量 据说还有种方法是连上$(t,s,in ...

  9. 【上下界网络流】bzoj2502: 清理雪道

    模型:无源汇有上下界可行流 LJN:模板题吧 Description        滑雪场坐落在FJ省西北部的若干座山上. 从空中鸟瞰,滑雪场可以看作一个有向无环图,每条弧代表一个斜坡(即雪道),弧的 ...

随机推荐

  1. 使用AVCaptureSession捕捉静态图片

    #import <UIKit/UIKit.h> #import <AVFoundation/AVFoundation.h> #import <AssetsLibrary/ ...

  2. ProGuard详解

    综述 对于ProGuard工具想必我们都不陌生,它能够通过移除无用代码,使用简短无意义的名称来重命名类,字段和方法.从而能够达到压缩.优化和混淆代码的目的.最终我们会获取一个较小的apk文件,并且我们 ...

  3. js 高阶函数 filter

    filter用于过滤array中的一些值,通过带入的函数返回的ture 或false 保留或去除,返回一个新的array filter 使用演示:判断筛选出array中的素数: //判断素数自定义函数 ...

  4. 使用PHP实现蜘蛛访问日志统计

    $useragent = addslashes(strtolower($_SERVER['HTTP_USER_AGENT'])); if (strpos($useragent, 'googlebot' ...

  5. hadoop-0.20-集群搭建___实体机通过SSH访问基于VM安装的Linux

    不得不说LZ在最开始搭建hadoop的时候,由于VM中的网段配置和本地IP地址没有配置好, 所以一直都在使用 VM的共享文件夹的功能, 以至于集群搭建好之后,只有namenode主机可以实现共享的功能 ...

  6. Orace数据库锁表的处理与总结<摘抄与总结一>

    TM锁(表级锁)类型共有5种,分别称为共享锁(S锁).排它锁(X锁).行级共享锁(RS锁).行级排它锁(RX锁).共享行级排它锁(SRX锁) 当Oracle执行DML语句时,系统自动在所要操作的表上申 ...

  7. jQuery 删除元素

    通过 jQuery,可以很容易地删除已有的 HTML 元素. 删除元素/内容 如需删除元素和内容,一般可使用以下两个 jQuery 方法: remove() - 删除被选元素(及其子元素) empty ...

  8. JS键盘码值表

    值得注意的是,keypress事件中获取的keycode.which,都是按键对应的ascii值,而不完全对应下面的列表. 将字符转换为ascii值可以用charCodeAt函数. keycode 8 ...

  9. java中的继承要点

    java的一大特性既是:继承. 1.因为有了一个子类继承了一个父类,才有了后面的多态. 2.类的继承,不要为了节省代码,为了继承而继承,把那个没有任何相关的类链接在一起,继承必须用在 is a,就是例 ...

  10. cocos2dx JAVA,C++互相调用函数

    C++调用JAVA 例子 #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) #include "platform/android/jni/Jni ...