BZOJ1070: [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
3 2
1 4
Sample Output
1.50
HINT
数据范围: (2<=M<=9,1<=N<=60), (1<=T<=1000)
Source
网络流的精彩之处就在于建图,这道题的建图方法就特别好玩。
首先,题目要求的是最小的顾客平均等待的时间,由于总的汽车数一定是n,所以我们只需要求出最小的总的等待时间就可以了。
由于每个人同一时间只能修1辆,每辆车修一次就够了(流量限制),用m个人修n次即可(流量守恒),有网络流的性质,而且求极值,所以想到用最小费用最大流解决。
但还有一个问题,边的权值只有一个,那如何合并时间(次序)与修车费用这两个维度呢?
由于可以同时工作,我们分别考虑每个人。假定 i 辆车是某人 倒数第 j 个修的,那么这辆车对总的等待时间的贡献就是 修理i的费用×j(修理时,有 j-1 辆车在等待其修理完成)
所以我们这样建图:
每个人i拆成多个点,表示这个人 倒数第k次修理车(k最大为n),对某个单点,与表示车辆j的点添加一条流量为1,权值为 k×cost[i][j]
为保证每辆车只修理一次,从表示车辆的点向汇点连一条流量为1的边即可
代码:
1 /* 2 * @FileName: /media/shan/Study/代码与算法/OJ(无法分类的题目)/BZOJ/1070/bzoj1070.cpp 3 * @Author: Pic 4 * @Created Time: 2017年11月21日 星期二 13时51分31秒 5 */ 6 #include <bits/stdc++.h> 7 using namespace std; 8 const int maxn=1000; 9 const int INF=0x3f3f3f3f; 10 struct Edge 11 { 12 int from,to,cap,flow,cost; 13 Edge(){} 14 Edge(int f,int t,int c,int fl,int co):from(f),to(t),cap(c),flow(fl),cost(co){} 15 }; 16 struct MCMF 17 { 18 int n,m,s,t; 19 vector<Edge> edges; 20 vector<int> G[maxn]; 21 bool inq[maxn]; //是否在队列 22 int d[maxn]; //Bellman_ford单源最短路径 23 int p[maxn]; //p[i]表从s到i的最小费用路径上的最后一条弧编号 24 int a[maxn]; //a[i]表示从s到i的最小残量 25 26 //初始化 27 void init(int n,int s,int t) 28 { 29 this->n=n, this->s=s, this->t=t; 30 edges.clear(); 31 for(int i=0;i<n;++i) G[i].clear(); 32 } 33 34 //添加一条有向边 35 void AddEdge(int from,int to,int cap,int cost) 36 { 37 edges.push_back(Edge(from,to,cap,0,cost)); 38 edges.push_back(Edge(to,from,0,0,-cost)); 39 m=edges.size(); 40 G[from].push_back(m-2); 41 G[to].push_back(m-1); 42 } 43 44 //求一次增广路 45 bool BellmanFord(int &flow, int &cost) 46 { 47 for(int i=0;i<n;++i) d[i]=INF; 48 memset(inq,0,sizeof(inq)); 49 d[s]=0, a[s]=INF, inq[s]=true, p[s]=0; 50 queue<int> Q; 51 Q.push(s); 52 while(!Q.empty()) 53 { 54 int u=Q.front(); Q.pop(); 55 inq[u]=false; 56 int len=G[u].size(); 57 for(int i=0;i<len;++i) 58 { 59 Edge &e=edges[G[u][i]]; 60 if(e.cap>e.flow && d[e.to]>d[u]+e.cost) 61 { 62 d[e.to]= d[u]+e.cost; 63 p[e.to]=G[u][i]; 64 a[e.to]= min(a[u],e.cap-e.flow); 65 if(!inq[e.to]){ Q.push(e.to); inq[e.to]=true; } 66 } 67 } 68 } 69 if(d[t]==INF) return false; 70 flow +=a[t]; 71 cost +=a[t]*d[t]; 72 int u=t; 73 while(u!=s) 74 { 75 edges[p[u]].flow += a[t]; 76 edges[p[u]^1].flow -=a[t]; 77 u = edges[p[u]].from; 78 } 79 return true; 80 } 81 82 //求出最小费用最大流 83 int Min_cost() 84 { 85 int flow=0,cost=0; 86 while(BellmanFord(flow,cost)); 87 return cost; 88 } 89 }MM; 90 int c[maxn][maxn]; 91 int main() 92 { 93 //freopen("data.in","r",stdin); 94 //freopen("data.out","w",stdout); 95 int m,n; 96 while(~scanf("%d%d",&m,&n)){ 97 int s=n*m+n+1; 98 int t=s+1; 99 MM.init(t+1,s,t); 100 for(int i=0;i<n;i++){ 101 for(int j=0;j<m;j++){ 102 scanf("%d",&c[i][j]); 103 } 104 } 105 for(int i=0;i<m;i++){ 106 for(int j=0;j<n;j++){ 107 for(int k=0;k<n;k++){ 108 MM.AddEdge(i*n+j,n*m+k,1,c[k][i]*(n-j)); 109 } 110 } 111 } 112 for(int i=0;i<n*m;i++){ 113 MM.AddEdge(s,i,1,0); 114 } 115 for(int i=n*m;i<n*m+n;i++){ 116 MM.AddEdge(i,t,1,0); 117 } 118 int res=MM.Min_cost(); 119 printf("%.2lf\n",res*1.0/n); 120 } 121 122 return 0; 123 }