POJ 2112 Optimal Milking ( 经典最大流 && Floyd && 二分 )
题意 : 有 K 台挤奶机器,每台机器可以接受 M 头牛进行挤奶作业,总共有 C 头奶牛,机器编号为 1~K,奶牛编号为 K+1 ~ K+C ,然后给出奶牛和机器之间的距离矩阵,要求求出使得每头牛都能被安排到某一挤奶机且所有奶牛走出来的路径的最大值的最小值。
分析 : 一个比较复杂的最小化最大值问题,解题思路是二分路程花费,然后建图使用最大流判断可行性。当然还可以使用最小费用最大流,增广和最短路的松弛维护的就是路径上的最大值而不再是花费了。这里只讨论二分+最大流解法,最小费用最大流的坑以后再填........
其实这题的思路和 POJ 2391 差不多 ==> 解题报告
先将题目给出来的距离矩阵跑一下 Floyd 求出全源最短路方便后面建图,这里注意一下除了对角线的点若有其他点为 0 则应将其值设置为 INF 代表不可达
抽象出一个源点和汇点,然后给安排出 C 个点代表 C 头牛、安排 K 个点代表 K 个挤奶机器,将源点到牛所代表的 C 个点各连一条容量为 1 的边
然后二分答案,对于二分出来的花费我们可以根据 Floyd 跑出来的距离矩阵将牛与机器之间的符合条件的( 最短花费 <= 当前二分的花费 )边连上
最后将各个机器与汇点连一条容量为 M 的边,以达到限制每台机器只接受 M 头牛这一限制,最后跑一下最大流,如果最大流 == 牛的总数说明可行
#include<stdio.h> #include<queue> #include<vector> #include<algorithm> #include<string.h> using namespace std; ; const int INF = 0x3f3f3f3f; int Dist[maxn][maxn]; int K, C, M; struct Edge { int from,to,cap,flow; Edge(){} Edge(int from,int to,int cap,int flow):from(from),to(to),cap(cap),flow(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(int Upper) { ; DC.init(N+, , N); ; i<=C; i++) DC.AddEdge(, K+i, ); ; i<=K; i++) DC.AddEdge(i, N, M); ; i<=K+C; i++) ; j<=K; j++) if(Dist[i][j] <= Upper) DC.AddEdge(i, j, INF); return (DC.Maxflow() == C); } int main(void) { while(~scanf("%d %d %d", &K, &C, &M)){ ; i<=K+C; i++) ; j<=K+C; j++){ scanf("%d", &Dist[i][j]); ) Dist[i][j] = INF; } // for(int i=1; i<=K+C; i++){ // for(int j=1; j<=K+C; j++){ // printf("%d ", Dist[i][j]); // }puts(""); // }puts(""); ; k<=K+C; k++) ; i<=K+C; i++) ; j<=K+C; j++) Dist[i][j] = min(Dist[i][j], Dist[i][k]+Dist[k][j]); // for(int i=1; i<=K+C; i++){ // for(int j=1; j<=K+C; j++){ // printf("%d ", Dist[i][j]); // }puts(""); // }puts(""); , FLOOR = INF; ; i<=K+C; i++) ; j<=K+C; j++){ if(i==j) continue; if(Dist[i][j] == INF) continue; UPPER = max(UPPER, Dist[i][j]); FLOOR = min(FLOOR, Dist[i][j]); } , mid; while(L <= R){ mid = L + ((R-L)>>); ; ; } printf("%d\n", ans); } ; }
瞎 : 之前是做过 POJ 2391 的,在思考这道题的时候大部分都能想出来,但是在具体实现的时候由于对此类解法的理解不够深厚,在写二分判断函数建边的时候我的代码如下
bool OK(int Upper) { ; DC.init(N+, , N); ; i<=C; i++) DC.AddEdge(, K+i, ); ; i<=K; i++) DC.AddEdge(i, N, M); ; i<=K+C; i++) ///这里我傻逼了...... ; j<=K+C; j++) if(Dist[i][j] <= Upper) DC.AddEdge(i, j, INF); return (DC.Maxflow() == C); }
当时是理解为从源点出发,然后所有的边去和当前二分答案判断是否加上这一条边,这样牛与牛、机器与机器可能就会连上,因为牛可以通过去其他牛所在的地方去其他机器或者通过其他机器所在的点去另外的机器,也许更优!其实很傻逼......,我没有理解深刻,实际上让原本的矩阵去跑 Floyd 就是做这个事情的,所以跑完 Floyd 之后直接将我们让想要的牛与机器之间最短花费与二分花费去判断,最后连成的是一个二分图。如果按我错误的做法那么跑 Floyd 便失去了意义,多连上了牛和牛的或者机器和机器的那么就有可能使得有些不符合条件的边也被连上!下面举个例子:
假设 ① 是机器且可接纳两头牛,②、③ 都是牛,假设当前二分的花费为 2
如果按我的错误做法实际 ①、② 之间的最小距离为 3 应该不可达,而①、③可以
但是由于我全局地去连边即我会去判断②、③之间是否花费小于当前二分出来的 2
此时会发现②和③的边被连上了,如果这样去跑最大流会被判定为满流,但是实际不是
所以正确做法是跑完 Floyd 之后只考虑牛和机器就行了
让我意识到这一点的是下面这个例子,建议把图画出来,然后AC代码跑出了我认为不可能跑出的3
下面的例子则是我特意将牛和机器之间的距离放大,然后将一组牛牛距离变小
因为我认为正确代码它只考虑了牛和机器,所以不对,但是事实你也看到了,我傻逼了
2 3 3
0 0 2 0 2
0 0 0 100 0
2 0 0 1 0
0 100 1 0 0
2 0 0 0 0
ans = 3
POJ 2112 Optimal Milking ( 经典最大流 && Floyd && 二分 )的更多相关文章
- POJ 2112—— Optimal Milking——————【多重匹配、二分枚举答案、floyd预处理】
Optimal Milking Time Limit:2000MS Memory Limit:30000KB 64bit IO Format:%I64d & %I64u Sub ...
- POJ 2391 Ombrophobic Bovines ( 经典最大流 && Floyd && 二分 && 拆点建图)
题意 : 给出一些牛棚,每个牛棚都原本都有一些牛但是每个牛棚可以容纳的牛都是有限的,现在给出一些路与路的花费和牛棚拥有的牛和可以容纳牛的数量,要求最短能在多少时间内使得每头牛都有安身的牛棚.( 这里注 ...
- POJ 2112 Optimal Milking(最大流)
题目链接:http://poj.org/problem?id=2112 Description FJ has moved his K (1 <= K <= 30) milking mach ...
- POJ 2112.Optimal Milking (最大流)
时间限制:2s 空间限制:30M 题意: 有K台挤奶机(编号1~K),C头奶牛(编号K+1~K+C),给出各点之间距离.现在要让C头奶牛到挤奶机去挤奶,每台挤奶机只能处理M头奶牛,求使所走路程最远的奶 ...
- POJ 2112 Optimal Milking 【网络流】【二分】【最短路】
题意: k c m 分别代表挤奶机数量,牛数量,和挤奶机容量. 接下来(n=k+c)n*n的矩阵A,代表挤奶机或者牛的距离,如果对角线都为0,如果非对角线没有直接路相连也为0. 1 <= K & ...
- POJ 2112 Optimal Milking (二分 + floyd + 网络流)
POJ 2112 Optimal Milking 链接:http://poj.org/problem?id=2112 题意:农场主John 将他的K(1≤K≤30)个挤奶器运到牧场,在那里有C(1≤C ...
- POJ 2112 Optimal Milking (二分+最短路径+网络流)
POJ 2112 Optimal Milking (二分+最短路径+网络流) Optimal Milking Time Limit: 2000MS Memory Limit: 30000K To ...
- Poj 2112 Optimal Milking (多重匹配+传递闭包+二分)
题目链接: Poj 2112 Optimal Milking 题目描述: 有k个挤奶机,c头牛,每台挤奶机每天最多可以给m头奶牛挤奶.挤奶机编号从1到k,奶牛编号从k+1到k+c,给出(k+c)*(k ...
- POJ 2112 Optimal Milking (Dinic + Floyd + 二分)
Optimal Milking Time Limit: 2000MS Memory Limit: 30000K Total Submissions: 19456 Accepted: 6947 ...
随机推荐
- MySQL单列索引和组合索引的创建及区别介绍
MySQL单列索引是我们使用MySQL数据库中经常会见到的,MySQL单列索引和组合索引的区别可能有很多人还不是十分的了解,下面就为您分析两者的主要区别,供您参考学习. 为了形象地对比两者,再建一个表 ...
- Java多线程学习——synchronized锁机制
Java在多线程中使用同步锁机制时,一定要注意锁对对象,下面的例子就是没锁对对象(每个线程使用一个被锁住的对象时,得先看该对象的被锁住部分是否有人在使用) 例子:两个人操作同一个银行账户,丈夫在ATM ...
- 应用安全 - 编程语言漏洞 - PHP语言漏洞汇总
CVE-2019-11043 Date: 类型: 远程代码执行 前置条件: Nginx + fastcgi + php-fpm 配置文件信息如下: location ~ [^/]\.php(/|$) ...
- 动态SQL之模糊查询
模糊查询学习了三种: DAO层 // 可以使用 List<User> wherelike01(String user_name); // 忘记 List<User> where ...
- 牛客练习赛46 E 华华和奕奕学物理 (树状数组)
https://ac.nowcoder.com/acm/contest/894/E 一开始写了一个简单的模拟 通过率只有5%...... 看题解真的理解了好久!!肥宅大哭orz 题解如下 最后一句:“ ...
- 学习C++的意义
1,常见的观点: 1,并不是每个应届生都有机会写操作系统和驱动程序: 2,嵌入式系统也是软家系统,只不过是软件在出厂的时候已经被烧写到硬件中了,用户没有办法修改软件而已,因此嵌入式系统也是软件系统,C ...
- 在centos6.4下安装python3.5
1.安装依赖包 ./configure --prefix=/usr/local/python3.5 --enable-shared make && make install yum g ...
- 剑指offer-树的子结构-python
题目描述 输入两棵二叉树A,B,判断B是不是A的子结构.(ps:我们约定空树不是任意一个树的子结构) 思路 空树不是任意一个树的子结构,如果 root1 与root2 中有一个为空树的话,返回 ...
- es6中let实例应用之一
有如下情景 html部分: <button class="btn">按钮1</button> <button class="btn" ...
- 简单的物流项目实战,WPF的MVVM设计模式(一)
新建一个WPF项目,命名为WMS 然后分别新建文件夹,Data,Models,Views,ViewModels,Services,如下图所示 然后通过NuGet安装连个Nuget包,分别为SQLite ...