POJ-2112 Optimal Milking(floyd+最大流+二分)

题目大意:

有k个挤奶器,在牧场里有c头奶牛,每个挤奶器可以满足m个奶牛,奶牛和挤奶器都可以看成是实体,现在给出两个实体之间的距离,如果没有路径相连,则为0,现在问你在所有方案里面,这c头奶牛需要走的最大距离的最小值。

分析:

先将题目给出来的距离矩阵跑一下 Floyd 求出全源最短路方便后面建图,

这里注意一下除了对角线的点若有其他点为 0 则应将其值设置为 INF 代表不可达

在使用最大流判断是否存在解的时候,要对每个解都重新建图。

建图需要一个超级源点,把所有的奶牛与源点相连,容量设置为1

把所有的挤奶器与汇点相连,容量为m

然后对于挤奶器和奶牛的距离不超过判断的解的距离的连边,容量设置为1

然后求解即可。如果最大流 == 牛的总数说明可行

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 3000;
const int INF = 0x3f3f3f3f;
int mp[maxn][maxn];
int L,R;
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;
        for(int i=0;i<=n;i++) G[i].clear();
        edges.clear();
    }

    void AddEdge(int from,int to,int cap)
    {
        edges.push_back( Edge(from,to,cap,0) );
        edges.push_back( Edge(to,from,0,0) );
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }

    bool BFS()
    {
        memset(vis,0,sizeof(vis));
        queue<int> Q;//用来保存节点编号的
        Q.push(s);
        d[s]=0;
        vis[s]=true;
        while(!Q.empty())
        {
            int x=Q.front(); Q.pop();
            for(int i=0; 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]+1;
                    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);
        if(x==t || a==0)return a;
        int flow=0,f;//flow用来记录从x到t的最小残量
        for(int& i=cur[x]; i<G[x].size(); i++)
        {
            Edge& e=edges[G[x][i]];
            if(d[x]+1==d[e.to] && (f=DFS( e.to,min(a,e.cap-e.flow) ) )>0 )
            {
                e.flow +=f;
                edges[G[x][i]^1].flow -=f;
                flow += f;
                a -= f;
                if(a==0) break;
            }
        }
        return flow;
    }

    int Maxflow()
    {
        int flow=0;
        while(BFS())
        {
            memset(cur,0,sizeof(cur));
            flow += DFS(s,INF);
        }
        return flow;
    }
}DC;
void FD(int K,int C)
{
    int n=K+C;
    L=INF,R=-INF;
    for(int k=1 ; k<=n ; k++)
    {
        for(int i=1 ; i<=n ; i++)
        {
            for(int j=1 ; j<=n ; j++)
            {
                mp[i][j]=min(mp[i][k]+mp[k][j],mp[i][j]);
                L=min(L,mp[i][j]);
                R=max(R,mp[i][j]);
            }

        }
    }
}
bool ok(int mid,int k,int c,int m)
{
    int n=k+c+1;
    DC.init(n+1,0,n);
    for(int i=1 ; i<=c ; i++)
    DC.AddEdge(0,k+i,1);
    for(int i=1 ; i<=k ; i++)
    DC.AddEdge(i,n,m);
    for(int i=k+1 ; i<=k+c ; i++)
    for(int j=1 ; j<=k ; j++)
    if(mp[i][j]<=mid)
    DC.AddEdge(i,j,INF);
    return (DC.Maxflow()==c);
}

int main( )
{
   int k,c,m;
   while(scanf("%d%d%d",&k,&c,&m)!=EOF)
   {
       for(int i=1 ; i<=k+c ; i++)
       for(int j=1 ; j<=k+c ; j++)
       {
           scanf("%d",&mp[i][j]);
           if(i!=j&&mp[i][j]==0)
            mp[i][j]=INF;
       }
       FD(k,c);
       int ans;
       while(L<=R)
       {
           int mid = (L+R)>>1;
           if(!ok(mid,k,c,m))
            L = mid+1;
           else
           {
               ans=mid;
               R=mid-1;
           }
       }
       printf("%d\n",ans);
   }
   return 0;
}
View Code

 

posted @ 2018-06-08 20:55  shuai_hui  阅读(135)  评论(0编辑  收藏  举报