bzoj1070 修车 最小费用流

1070: [SCOI2007]修车

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 5655   Solved: 2383
[ Submit][ Status][ Discuss]

Description

  同一时刻有N位车主带着他们的爱车来到了汽车维修中心。维修中心共有M位技术人员,不同的技术人员对不同
的车进行维修所用的时间是不同的。现在需要安排这M位技术人员所维修的车及顺序,使得顾客平均等待的时间最
小。 说明:顾客的等待时间是指从他把车送至维修中心到维修完毕所用的时间。

Input

  第一行有两个m,n,表示技术人员数与顾客数。 接下来n行,每行m个整数。第i+1行第j个数表示第j位技术人
员维修第i辆车需要用的时间T。

Output

  最小平均等待时间,答案精确到小数点后2位。

Sample Input

2 2
3 2
1 4

Sample Output

1.50

HINT

数据范围: (2<=M<=9,1<=N<=60), (1<=T<=1000)


题目思路:

       首先分析问题,最终状态应该是把来修车的n个人分成m队,也就是每个修车师傅面前排一队人,其中这个队里的第k个人等待时间是k*这个人在该技术工人这里修车的时间T;

       那么,也就是说把这n个人分配到m个队中的某个位置上,每个人只分配一次,每个位置只有一个人。

       因此,可以用网络流来解决。图中共有1个源点,1个汇点,n个节点代表n个人(人节点 ),n*m个节点代表每个队列中的每个位置(位置节点)。从源点向每个人节点连一条容量为1,费用是0的边;从每个人节点向每个位置节点连一条边,容量为1,费用是这个人站在这个队列的这个位置时的花费;每个位置节点向汇点连一条容量是1,费用是0的边。跑一遍源点到汇点的最小费用最大流得到答案。

      看了一下status,速度差距明显,查了一下网上题解的做法,认为有必要学一下zkw费用流。

#pragma warning(disable:4786)
#pragma comment(linker, "/STACK:102400000,102400000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<cmath>
#include<string>
#include<sstream>
#include<bitset>
#define LL long long
#define FOR(i,f_start,f_end) for(int i=f_start;i<=f_end;++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define lson l,m,x<<1
#define rson m+1,r,x<<1|1
using namespace std;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const double PI = acos(-1.0);
const double eps=1e-6;
const int maxn = 700;
int c[65][15];
struct Edge
{
    int from , to , cap , flow , cost;
};
struct MCMF
{
    int n, m ,s , t;
    vector<Edge>edges;
    vector<int>G[maxn];
    int d[maxn] , p[maxn] , a[maxn] , inq[maxn];

    void init(int n){
        this -> n = n;
        for(int i = 0 ; i <= n ; i++)       G[i].clear();
        edges.clear();
    }

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

    bool BellmanFord(int s , int t , int & flow , LL & cost){
        mem(d , INF);
        d[s] = 0;   p[s] = 0;   a[s] = INF;
        mem(inq , 0);   inq[s] = 1;
        queue<int>Q;
        Q.push(s);
        while(!Q.empty()){
            int x = Q.front();      Q.pop();
            inq[x] = 0;
            for(int i = 0 ; i < G[x].size() ; i++){
                Edge & e = edges[G[x][i]];
                if(e.cap > e.flow && d[e.to] > d[x] + e.cost){
                    d[e.to] = d[x] + e.cost;
                    p[e.to] = G[x][i];
                    a[e.to] = min(a[x] , e.cap - e.flow);
                    if(!inq[e.to]){
                        Q.push(e.to);
                        inq[e.to] = 1;
                    }
                }
            }
        }
        if(d[t] == INF)     return false;
        flow += a[t];
        cost += (LL)a[t] * (LL)d[t];
        int u = t;
        while(u != s){
            edges[p[u]].flow += a[t];
            edges[p[u]^1].flow -= a[t];
            u = edges[p[u]].from;
        }
        return true;
    }

    LL Mincost(int s , int t , int & flow){
        flow = 0;
        LL cost = 0;
        while(BellmanFord(s , t , flow , cost))     ;
        return cost;
    }
}g;
int main()
{
    int n , m;
    while(scanf("%d %d" , &m , &n) != EOF){
        for(int i = 1 ; i <= n ; i++){
            for(int j = 1 ; j <= m ; j++){
                scanf("%d" , &c[i][j]);
            }
        }
        g.init(2 + n * m + n);
        int source = 1 , sink = 2 + n * m + n;
        for(int i = 1 ; i <= n; i++){
            g.AddEdge(source , 1 + i , 1 , 0);
            for(int j = 1 ; j <= m ; j++){
                for(int k = 1 ; k <= n; k++){
                    g.AddEdge(i + 1 , 1 + n + n * (j - 1) + k , 1 , k * c[i][j]);
                }
            }
        }
        for(int j = 1 ; j <= m ; j++){
            for(int k = 1 ; k <= n; k++){
                g.AddEdge(1 + n + n * (j - 1) + k , sink , 1 , 0);
            }
        }
        int flow ;
        double ans = (double)g.Mincost(source , sink , flow);
        ans = ans / n;
        printf("%.2lf\n" , ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值