bzoj1070【SCOI2007】修车(费用流)

题目描述

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

说明:顾客的等待时间是指从他把车送至维修中心到维修完毕所用的时间。

输入输出格式

输入格式:

 

第一行有两个数M,N,表示技术人员数与顾客数。

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

 

输出格式:

 

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

 

输入输出样例

输入样例#1: 复制
2 2
3 2
1 4
输出样例#1: 复制
1.50

说明

(2<=M<=9,1<=N<=60), (1<=T<=1000)

题解

  这篇还是写得详细一点好了……因为不是很懂……

  我们考虑一下,如果一个工人修车的序列为$W_1,W_2,W_3...W_n$

  那么对于这几辆车的车主而言,他们等待的总时间是$\sum _{i=1}^n W_i*(n-i+1)=nW_1+(n-1)W_2+...+W_n$(因为一个人在越前面修,会使后面更多的人要等待他的车修好)

  然后因为平均时间最少,人数是不变的,所以得保证总时间最少

  我们发现,如果把第$i$个人的车让第$j$个人在倒数第$k$个修(以下表示为$(i,j,k)$),那么对总时间的贡献是$T(i,j)*k$,其中$T(i,j)$表示第$j$个人修第$i$辆车的时间

  然后因为每一辆车只能被一个人修,每一个人同一时间只能修一辆车

  那么我们可以把$(j,k)$表示成一个状态,表示被第$j$个人在倒数第$k$个修,那么不难发现每一个状态只能被匹配一次,即不可能有两辆车同时被一个人在同一个顺序修

  那么我们可以建一个二分图,左边是$n$辆车,右边是$n*m$个状态$(j,k)$(因为$k$不可能超过$n$),然后左边的每一个点向右边所有点连边,容量为$1$,费用为对应的$(i,j,k)$

  然后因为每一辆车只会被修一次,所以从源点向所有车连容$1$费$0$的边

  因为每一个人在同一时间只能修一辆车,所以右边所有状态向汇点连容$1$费$0$的边

  当网络跑满的时候说明所有车都有人修了,然后又要时间最少,只要在此基础上求一个最小费用流即可

 1 //minamoto
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<queue>
 5 #include<cstring>
 6 #define inf 0x3f3f3f3f
 7 #define id(i,j) (i-1)*n+j
 8 using namespace std;
 9 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
10 char buf[1<<21],*p1=buf,*p2=buf;
11 inline int read(){
12     #define num ch-'0'
13     char ch;bool flag=0;int res;
14     while(!isdigit(ch=getc()))
15     (ch=='-')&&(flag=true);
16     for(res=num;isdigit(ch=getc());res=res*10+num);
17     (flag)&&(res=-res);
18     #undef num
19     return res;
20 }
21 const int N=1005,M=100005;
22 int ver[M],Next[M],head[N],edge[M],flow[M],tot=1;
23 int vis[N],dis[N],disf[N],Pre[N],last[N],n,m,s,t;
24 queue<int> q;
25 inline void add(int u,int v,int f,int e){
26     ver[++tot]=v,Next[tot]=head[u],head[u]=tot,flow[tot]=f,edge[tot]=e;
27     ver[++tot]=u,Next[tot]=head[v],head[v]=tot,flow[tot]=0,edge[tot]=-e;
28 }
29 bool spfa(){
30     memset(dis,0x3f,sizeof(dis));
31     while(!q.empty()) q.pop();
32     q.push(s),dis[s]=0,disf[s]=inf,Pre[t]=-1;
33     while(!q.empty()){
34         int u=q.front();q.pop(),vis[u]=0;
35         for(int i=head[u];i;i=Next[i]){
36             int v=ver[i];
37             if(flow[i]&&dis[v]>dis[u]+edge[i]){
38                 dis[v]=dis[u]+edge[i],Pre[v]=u,last[v]=i;
39                 disf[v]=min(disf[u],flow[i]);
40                 if(!vis[v]) vis[v]=1,q.push(v);
41             }
42         }
43     }
44     return ~Pre[t];
45 }
46 int dinic(){
47     int mincost=0;
48     while(spfa()){
49         int u=t;mincost+=disf[t]*dis[t];
50         while(u!=s){
51             flow[last[u]]-=disf[t],flow[last[u]^1]+=disf[t];
52             u=Pre[u];
53         }
54     }
55     return mincost;
56 }
57 int main(){
58     m=read(),n=read();
59     s=0,t=n*m+n+1;
60     for(int i=1;i<=n;++i) add(s,i,1,0);
61     for(int i=1;i<=m;++i)
62     for(int j=1;j<=n;++j)
63     add(n+id(i,j),t,1,0);
64     for(int i=1;i<=n;++i)
65     for(int j=1;j<=m;++j){
66         int cost=read();
67         for(int k=1;k<=n;++k){
68             add(i,n+id(j,k),1,cost*k);
69         }
70     }
71     printf("%.2lf",(double)dinic()/n);
72     return 0;
73 }

 

posted @ 2018-08-21 12:40  bztMinamoto  阅读(186)  评论(0编辑  收藏  举报
Live2D