POJ 2391 Ombrophobic Bovines ( 经典最大流 && Floyd && 二分 && 拆点建图)
题意 : 给出一些牛棚,每个牛棚都原本都有一些牛但是每个牛棚可以容纳的牛都是有限的,现在给出一些路与路的花费和牛棚拥有的牛和可以容纳牛的数量,要求最短能在多少时间内使得每头牛都有安身的牛棚。( 这里注意给出的边是无向边且有重边 )
分析 : 听说是网络流的经典题型,这里先来讲一下如何转化为最大流然后二分求解。
① 先来说一下最大流在这题 ”扮演的角色"
先不考虑牛棚之间花费的关系,先抽象出原本没有的两个点,一个源点和一个汇点,设置源点到各个牛棚边的容量为牛棚原有的牛数,设置各个牛棚到汇点边的容量为各个牛棚的容量,然后将之间有路连接的牛棚都连上一条容量为无穷大的边 ( 但是后面实际不会这样做,需要拆点,现在可以先这样理解 ),这样建图后从源点到汇点跑出来的最大流如果等于全部牛的数量则说明可行 ( 即满流情况 ),否则不行。至此我们知道可以利用最大流的知识来判断当前建的一副图是否是一个可行解,由于时间越多满足条件的可能性越大,所以可以二分时间再用最大流来作为判断工具检查当前二分出来的时间是否可行。
② 和最短路算法的关系
既然现在是二分时间,那么必定有一些边是无法走的或者说有些点是不可互达的 ( 这些不符合条件的边或者两点间花费是大于当前二分出来的时间的 ),那么就要求求出这些两点间的最小花费,自然想到 Floyd 算法求全源最短路,每一次二分答案都根据 Floyd 跑出来的结果重新建图去跑最大流检查可行性 ( 但是具体要怎么做呢?不妨现在暂停一下想想? )
③ 具体实现以及为何拆点建图?
其实根据刚刚 ①、② 的叙述,你可能?会想为什么不用最小费用最大流来求解呢?为什么要重新建图再跑最大流?
这里的时间是个时间覆盖问题,也就是说在给出的时间内可能有多头牛从不同点在进行移动!!这和在路径上跑出来的花费积累是不一样的!!仔细想想!!
在根据二分时间建图的过程中,如果就直接在原图上建,是起不到限制的作用的!以下引用 此论文 的解释
一种错误的建图方法,即不拆点,见下图:
其中每条无向边表示两条方向相反的有向边,容量均为∞。
( 1 为源点、5 为汇点 )
当二分到 T = 70的时候,显然我们只加入了 (2, 3) 和 (3, 4) 两条无向边,因为只有这两对点间的最短距离小于等于 70。
但是从图中也可以看出,由于没有拆点,点 2 也可以通过这两条边到达点 4,而实际上这是不允许的。也就是说我们所加
的限制条件没有起到作用。由此可见,只有拆点才是正确的做法。
拆点的正确做法则是将原本的 v 点拆成 v 与 v' ,源点和各个 v 相连、汇点和各个 v'
如果 Dis[u][v] <= 当前二分时间 则连 u 与 v' ,就避免了与 v 相连产生干扰。
其实这样连出来的图为一个二分图 or 二部图。
代码实现则是多开 N 个点即可,即 原本编号为 1~n 的点拆成 1~n 和 n+1~2*n ,此时 v' = v + n
其实这题最后就归结为最大流下的最大距离最小化问题,一般此种类型都用二分解决。
#include<bits/stdc++.h> #define LL long long using namespace std; ; const int INF = 1e9; ; LL Dist[maxn][maxn]; int Have[maxn], Can[maxn]; int N, M; int Full_Flow; LL Limit; struct Edge { Edge(){} Edge(int from,int to,int cap,int flow):from(from),to(to),cap(cap),flow(flow){} int from,to,cap,flow; }; struct Dinic { int n,m,s,t; //结点数,边数(包括反向弧),源点与汇点编号 vector<Edge> edges; //边表 edges[e]和edges[e^1]互为反向弧 vector<int> G[maxn]; //邻接表,G[i][j]表示结点i的第j条边在e数组中的序号 bool vis[maxn]; //BFS使用,标记一个节点是否被遍历过 int d[maxn]; //d[i]表从起点s到i点的距离(层次) int cur[maxn]; //cur[i]表当前正访问i节点的第cur[i]条弧 void init(int n,int s,int t) { this->n=n,this->s=s,this->t=t; ;i<=n;i++) G[i].clear(); edges.clear(); } void AddEdge(int from,int to,int cap) { edges.push_back( Edge() ); edges.push_back( Edge(to,,) ); m = edges.size(); G[); G[to].push_back(m-); } bool BFS() { memset(vis,,sizeof(vis)); queue<int> Q;//用来保存节点编号的 Q.push(s); d[s]=; vis[s]=true; while(!Q.empty()) { int x=Q.front(); Q.pop(); ; i<G[x].size(); i++) { Edge& e=edges[G[x][i]]; if(!vis[e.to] && e.cap>e.flow) { vis[e.to]=true; d[e.to] = d[x]+; Q.push(e.to); } } } return vis[t]; } //a表示从s到x目前为止所有弧的最小残量 //flow表示从x到t的最小残量 int DFS(int x,int a) { //printf("%d %d\n", x, a); )return a; ,f;//flow用来记录从x到t的最小残量 for(int& i=cur[x]; i<G[x].size(); i++) { Edge& e=edges[G[x][i]]; ==d[e.to] && (f=DFS( e.to,min(a,e.cap-e.flow) ) )> ) { e.flow +=f; edges[G[x][i]^].flow -=f; flow += f; a -= f; ) break; } } return flow; } int Maxflow() { ; while(BFS()) { memset(cur,,sizeof(cur)); flow += DFS(s,INF); } return flow; } }DC; bool OK(LL Upper) { DC.init(*N+, , *N+); ; i<=N; i++){ DC.AddEdge(, i, Have[i]); DC.AddEdge(i+N, *N+, Can[i]); } ; i<=N; i++) ; j<=N; j++) if(Dist[i][j] <= Upper) DC.AddEdge(i, j+N, INF); return (Full_Flow == DC.Maxflow()); } int main(void) { while(~scanf("%d %d", &N, &M)){ Full_Flow = ; Limit = ; ; i<=N; i++){ scanf("%d %d", &Have[i], &Can[i]); Full_Flow += Have[i];///记录所有牛的数量 } ; i<=N; i++) ; j<=N; j++)///跑 Floyd 前的初始化 ; else Dist[i][j] = INF_LL; int From, To; LL Cost; ; i<=M; i++){ scanf("%d %d %lld", &From, &To, &Cost); Dist[From][To] = Dist[To][From] = min(Dist[From][To], Cost);///有重边,只需记录最小的花费那条边 } ; k<=N; k++)///Floyd 算法 ; i<=N; i++) ; j<=N; j++) if(Dist[i][k] < INF_LL && Dist[k][j] < INF_LL) Dist[i][j] = min(Dist[i][j], Dist[i][k]+Dist[k][j]); Limit = ;///再次强调这里的时间是覆盖时间,所以我们找出花费最大的两点互大花费作为二分上界 ; i<=N; i++) ; j<=N; j++) if(Dist[i][j] < INF_LL) Limit = max(Limit, Dist[i][j]); if(!OK(Limit)) puts("-1");///给出最充裕的时间都无法满流肯定是无解了 else{ LL L = , R = Limit, ans; while(L <= R){///二分答案 LL mid = L + ((R-L)>>); ; ; } printf("%lld\n", ans); } } ; }
POJ 2391 Ombrophobic Bovines ( 经典最大流 && Floyd && 二分 && 拆点建图)的更多相关文章
- POJ 2391.Ombrophobic Bovines (最大流)
实际上是求最短的避雨时间. 首先将每个点拆成两个,一个连接源点,一个连接汇点,连接源点的点的容量为当前单的奶牛数,连接汇点的点为能容纳的奶牛数. floyd求任意两点互相到达的最短时间,二分最长时间, ...
- POJ 2112 Optimal Milking ( 经典最大流 && Floyd && 二分 )
题意 : 有 K 台挤奶机器,每台机器可以接受 M 头牛进行挤奶作业,总共有 C 头奶牛,机器编号为 1~K,奶牛编号为 K+1 ~ K+C ,然后给出奶牛和机器之间的距离矩阵,要求求出使得每头牛都能 ...
- poj 2391 Ombrophobic Bovines【最大流】
我%--&(¥--,调了一下午,最后发现P赋值1e5能过,赋值1e6就会TLE致死.改了一下午加一晚上然而这是为什么??? 一种常见的建图套路,首先二分答案,注意上界要取大一点,1e9是不行的 ...
- poj 2391 Ombrophobic Bovines, 最大流, 拆点, 二分, dinic, isap
poj 2391 Ombrophobic Bovines, 最大流, 拆点, 二分 dinic /* * Author: yew1eb * Created Time: 2014年10月31日 星期五 ...
- POJ 2391 Ombrophobic Bovines ★(Floyd+二分+拆点+最大流)
[题意]有n块草地,一些奶牛在草地上吃草,草地间有m条路,一些草地上有避雨点,每个避雨点能容纳的奶牛是有限的,给出通过每条路的时间,问最少需要多少时间能让所有奶牛进入一个避雨点. 和POJ2112很类 ...
- 【bzoj1738】[Usaco2005 mar]Ombrophobic Bovines 发抖的牛 Floyd+二分+网络流最大流
题目描述 FJ's cows really hate getting wet so much that the mere thought of getting caught in the rain m ...
- POJ 2391 Ombrophobic Bovines 网络流 建模
[题目大意]给定一个无向图,点i处有Ai头牛,点i处的牛棚能容纳Bi头牛,求一个最短时间T使得在T时间内所有的牛都能进到某一牛棚里去.(1 <= N <= 200, 1 <= M & ...
- poj 2391 Ombrophobic Bovines(最大流+floyd+二分)
Ombrophobic Bovines Time Limit: 1000MSMemory Limit: 65536K Total Submissions: 14519Accepted: 3170 De ...
- POJ 2391 Ombrophobic Bovines (二分答案+floyd+最大流)
<题目链接> 题目大意: 给定一个有$n$个顶点和$m$条边的无向图,点$i$ 处有$A_i$头牛,点$i$ 处的牛棚能容纳$B_i$头牛,每条边有一个时间花费$t_i$(表示从一个端点走 ...
随机推荐
- Chapter03 第三节 浮点数
3.3 浮点数 3.3.1 浮点数的表示 常规表示:12.34.0.01.8.0 E表示: 2.5e+8(2.5 10^8).7E6(7.0 10^6) (e大小写随意) (e+x或者E-x表示小数点 ...
- js函数的定义和调用
函数的定义 函数使用function 声明,后跟一组参数以及函数体,语法如下: function functionName([age0,age1,......argn]){ statements } ...
- oracle中与mysql中的命令 show databases, show tables, desc table类似的命令集
1 怎样执行一个sql脚本文件,这个脚本文件写了一系列的sql语句集,比如sql.sql 放在D:\MyEclipse 8.6\Workspaces\OASystem\WebRoot\sql.sql下 ...
- 【Linux-设备树】.dtb文件的反汇编
在使用设备树时我们将**.dts文件利用dtc编译器编译为**.dtb文件. 在已知**.dtb文件的情况下我们有两种方法可以得到dts源码: 方法一:使用fdtdump工具进行反汇编 使用命令:ro ...
- 【监控笔记】【2.4】SQL Server中的 Ring Buffer 诊断各种系统资源压力情况
SQL Server 操作系统(SQLOS)负责管理特定于SQL Server的操作系统资源. 其中相关的动态管理试图sys.dm_os_ring_buffers将被标识为仅供参考.不提供支持.不保证 ...
- P2672跳石头
这是2015noip的一道二分答案的题目,看了题解才会,, 题目给出石头的位置并且让你踩着石头往前跳,最多删掉m个石头还可以顺利通过,求解最短跳跃距离的最大值. 那么二分什么呢:mid为跳跃的长度.那 ...
- Python 入门之格式化输出
Python 入门之格式化输出 1.格式化 (1)%为占位 (2)%s --- 站字符串的位置(数字.字符串都能够进行填充) name = input('请输入姓名:') age = input('请 ...
- 通过实例简介python使用ctypes模块调用C语言动态库
看介绍python语言时,说它是胶水语言,可以调用其他语言.通过使用ctypes模块就可以调用C语言的动态库.下面先放上官方文档和几个比较好的博文. 1.官方文档:http://python.net/ ...
- springboot 配置
springboot 配置文件中属性变量引用方式@@解析 这种属性应用方式是field_name=@field_value@. 两个@符号是springboot为替代${}属性占位符产生,原因是${} ...
- time和datetime的区别
time在 Python 文档里,time是归类在Generic Operating System Services中,换句话说, 它提供的功能是更加接近于操作系统层面的.通读文档可知,time 模块 ...