bzoj1070【SCOI2007】修车(费用流)
题目描述
同一时刻有N位车主带着他们的爱车来到了汽车维修中心。维修中心共有M位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的。现在需要安排这M位技术人员所维修的车及顺序,使得顾客平均等待的时间最小。
说明:顾客的等待时间是指从他把车送至维修中心到维修完毕所用的时间。
输入输出格式
输入格式:
第一行有两个数M,N,表示技术人员数与顾客数。
接下来n行,每行m个整数。第i+1行第j个数表示第j位技术人员维修第i辆车需要用的时间T。
输出格式:
最小平均等待时间,答案精确到小数点后2位。
输入输出样例
说明
(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 }