【BZOJ】1070: [SCOI2007]修车

1070: [SCOI2007]修车

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)

思路:开始以为排序之后模拟贪心,但是车的数量太多...每次对于一个工人来说,并不是直接按照他修理车的最短时间给他分配车的,还要看里面各种..修车时间差与等待总时间等因素;所以要用到网络流构图,让计算机不断的调整选择方案(增广路模拟);即最小费用最大流问题;

暴力构图:每个工人可能修理的车的数量即为车的数量,并且每次修理车的时间并不只是输入的时间,因为对于同一个工人来说其前面修理的车的时间会累加到后面修车的时间里面;但是一般这样前面对后面产生的次数影响,并不是对当前的车去看它前面修了哪些车,然后把这些车的总时间加上自己的时间代价的,因为这样还要不断记录与修改修车的顺序。。与其后面每次来找前面的麻烦。。还不如前面修理的时候直接计算出对后面的影响~~ 即该车是同一个工人倒数第k次修的,那么这辆车的总代价就是k*time[i][j];这样就有了暴力拆点的依据了~~

暴力拆点:把每个工人拆成m个点,第k个点表示是倒数第k次修的该车,至于该车就修改每辆车与n*m个点连边,边的容量为1,表示只修一次费用为time[i]][j]*k;至于与超级源点和汇点连边时,cap为1,但是费用记得要记为0;这样直接跑最大流即可;

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define MS0(a) memset((a),0,sizeof(a))
#define MSi(a) memset((a),0x3f,sizeof(a))
#define MS1(a) memset((a),-1,sizeof(a))
#define inf 0x3f3f3f3f
const int T = 1001;
const int M = 50000;
const int N = 1007;
int tot = 1,head[N];
struct edge{
    int from,to,Next,cap,cost;
}e[M<<1];
void ins(int u,int v,int cap,int cost)
{
    e[++tot].Next = head[u];// tot从2开始,之后增广边还是可以异或~~并且head并不需要设为-1.
    e[tot].from = u, e[tot].to = v;
    e[tot].cap = cap; e[tot].cost = cost;
    head[u] = tot;
}
void insert(int u,int v,int cap,int cost)//把cap当成费用;之后增广路中只是改变flow;
{
    ins(u,v,cap,cost);ins(v,u,0,-cost);
}
int t[61][10],dis[N],vis[N],p[N];
queue<int> que;
int spfa()
{
    MSi(dis);
    que.push(0);
    p[0] = 0;dis[0] = 0;
    while(!que.empty()){
        int u = que.front();que.pop();
        vis[u] = 0;
        for(int d = head[u];d ;d = e[d].Next){
            int v = e[d].to;           
            if(e[d].cap && dis[v] > dis[u] + e[d].cost){              
                dis[v] = dis[u]+e[d].cost;
                p[v] = d;
                if(!vis[v]){
                    que.push(v);
                    vis[v] = 1;
                }
            }
        }
    }  
    if(dis[T] == inf) return 0;
    return 1;
}
int ans;
int mcf()
{
    int v = inf;
    for(int d = p[T];d;d = p[e[d].from])
        v = min(v,e[d].cap);
    for(int d = p[T];d;d = p[e[d].from]){
        e[d].cap -= v; e[d^1].cap += v;
        ans += e[d].cost;
    }
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= m;i++)
        for(int j = 1;j <= n;j++)
            scanf("%d",&t[i][j]);
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= m;j++)
            insert(0,(i-1)*m+j,1,0);
    for(int i = n*m+1;i <= n*m+m;i++)
        insert(i,T,1,0);
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= m;j++)//第i个工人修的倒数第j辆车;
            for(int k = 1;k <= m;k++)
                insert((i-1)*m+j,n*m+k,1,t[k][i]*j);
    while(spfa()) mcf();
    printf("%.2f",1.*ans/m);
}

 

你可能感兴趣的:(【BZOJ】1070: [SCOI2007]修车)