题意 : 有 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 && 二分 )的更多相关文章

  1. POJ 2112—— Optimal Milking——————【多重匹配、二分枚举答案、floyd预处理】

    Optimal Milking Time Limit:2000MS     Memory Limit:30000KB     64bit IO Format:%I64d & %I64u Sub ...

  2. POJ 2391 Ombrophobic Bovines ( 经典最大流 && Floyd && 二分 && 拆点建图)

    题意 : 给出一些牛棚,每个牛棚都原本都有一些牛但是每个牛棚可以容纳的牛都是有限的,现在给出一些路与路的花费和牛棚拥有的牛和可以容纳牛的数量,要求最短能在多少时间内使得每头牛都有安身的牛棚.( 这里注 ...

  3. POJ 2112 Optimal Milking(最大流)

    题目链接:http://poj.org/problem?id=2112 Description FJ has moved his K (1 <= K <= 30) milking mach ...

  4. POJ 2112.Optimal Milking (最大流)

    时间限制:2s 空间限制:30M 题意: 有K台挤奶机(编号1~K),C头奶牛(编号K+1~K+C),给出各点之间距离.现在要让C头奶牛到挤奶机去挤奶,每台挤奶机只能处理M头奶牛,求使所走路程最远的奶 ...

  5. POJ 2112 Optimal Milking 【网络流】【二分】【最短路】

    题意: k c m 分别代表挤奶机数量,牛数量,和挤奶机容量. 接下来(n=k+c)n*n的矩阵A,代表挤奶机或者牛的距离,如果对角线都为0,如果非对角线没有直接路相连也为0. 1 <= K & ...

  6. POJ 2112 Optimal Milking (二分 + floyd + 网络流)

    POJ 2112 Optimal Milking 链接:http://poj.org/problem?id=2112 题意:农场主John 将他的K(1≤K≤30)个挤奶器运到牧场,在那里有C(1≤C ...

  7. POJ 2112 Optimal Milking (二分+最短路径+网络流)

    POJ  2112 Optimal Milking (二分+最短路径+网络流) Optimal Milking Time Limit: 2000MS   Memory Limit: 30000K To ...

  8. Poj 2112 Optimal Milking (多重匹配+传递闭包+二分)

    题目链接: Poj 2112 Optimal Milking 题目描述: 有k个挤奶机,c头牛,每台挤奶机每天最多可以给m头奶牛挤奶.挤奶机编号从1到k,奶牛编号从k+1到k+c,给出(k+c)*(k ...

  9. POJ 2112 Optimal Milking (Dinic + Floyd + 二分)

    Optimal Milking Time Limit: 2000MS   Memory Limit: 30000K Total Submissions: 19456   Accepted: 6947 ...

随机推荐

  1. Web UI自动化测试基础——元素定位(三)

    本篇文章整理了元素定位的基础知识——iframe框架中的元素定位. 一.iframe框架元素定位 iframe是Html页面的内联框架,如果在自动化测试中无法定位到某个元素,那么很有可能是因为该元素在 ...

  2. 初学node.js-nodejs中实现修改用户路由

    经过前面几次的学习,已经可以做下小功能,今天要实现的是修改用户路由. 一.users_model.js  功能:定义用户对象模型 var mongoose=require('mongoose'), S ...

  3. 华硕RT-AC86U路由器 AP模式实现多路由器组网,扩展主路由器的无线网范围

    描述: 宽带拨号上网的路由器为 TP-LINK  TL-WAR1200L,由于室内空间大,遂在此路由器下接入一个 华硕RT-AC86U路由器: 配置使该 华硕路由器与 TP-LINK 路由器的网段相同 ...

  4. editText内容从hint右输入

    如何让editText内容从hint右输入呢: <EditText android:id="@+id/et_password" android:textColor=" ...

  5. 【监控笔记】【2.3】扩展事件——慢查询SQL(执行超过3S的SQL)

    --sql server 2008及以上才支持,2012及以上才支持GUI界面 msdn 扩展事件:点击打开链接 [1]T-SQL实现 基于 rpc_completed(远程过程调用已完成时发生) 事 ...

  6. (3.5)常用知识-NULL与零长度、字符串尾部填充空格

    概述:NULL与零长度是不同的,NULL表示数据未知或不可用,它是与零(数值或2进制).零长度字符串不 同的一种值,也可以理解为一种状态. 即可以理解为:所有的变量都有2种状态,一种有值,一种为NUL ...

  7. 主机(windows10)虚拟机(ubuntu18)arm板(linux3.4)相互ping通

    实际中在主机上安装虚拟机,并在主机上通过网线连接arm板进行调试. 用网线将主机和arm板直接物理连接,且主机和arm必须处于同一个网段.(我们知道主机中的网卡具有路由器的功能) 其中arm板IP地址 ...

  8. mongodb导出导入数据

    在使用mongodump导出单个表的时候,遇到了一个错误 # mongodump --host xxx --port 27017 --username 'admin' -p '123456' -d 数 ...

  9. ASP.NET中数据库数据导入Excel并打印(2)

    大家可能觉得上面的代码比较复杂,因为上面对于对打印要求比较高的应用,是十分有效的.如果只是单单对数据进行导出,还可以使用简单的格式,比如使用以下的代码:      Private Sub Page_L ...

  10. 2019 红帽杯 Re WP

    0x01 xx 测试文件:https://www.lanzous.com/i7dyqhc 1.准备 获取信息 64位文件 2.IDA打开 使用Findcrypt脚本可以看到 结合文件名是xx,因此猜测 ...