Description
同一时刻有N位车主带着他们的爱车来到了汽车维修中心。维修中心共有M位技术人员,不同的技术人员对不同
的车进行维修所用的时间是不同的。现在需要安排这M位技术人员所维修的车及顺序,使得顾客平均等待的时间最
小。 说明:顾客的等待时间是指从他把车送至维修中心到维修完毕所用的时间。
Input
第一行有两个m,n,表示技术人员数与顾客数。 接下来n行,每行m个整数。第i+1行第j个数表示第j位技术人
员维修第i辆车需要用的时间T。
Output
最小平均等待时间,答案精确到小数点后2位。
Sample Input
3 2
1 4
Sample Output
HINT
数据范围: (2<=M<=9,1<=N<=60), (1<=T<=1000)
Source
Solution
我目前为止一道费用流的模型都不会建T_T
构造一个图,左边$n*m$个点,表示第$j$个技术人员修的倒数第$i$辆车,右边$n$个点,表示第$k$辆车
源点向左边的点连$(w=1, cost=0)$的边,因为每个人同一时刻只能修一辆车
左边$m*n$个点再分别向右边$n$个点连边,左边第$(i, j)$个点向右边第$k$个点连$(w=INF, cost=i*t[i][j])$的边
相当于此时有$i$个人在等待着这辆车修完,代价就是等待的人数$*$修这辆车的时间
右边的点向汇点连$(w=1, cost=0)$的边,表示每辆车只能被一个人修
容易看出,此时最大流一定是$n$,每一条$w=1$的流表示第$j$个人修的倒数第$i$辆车是第$k$辆车
那么最小费用除以$n$就是我们的答案
总点数是$2+m*n+n$,不超过$602$;边数是$2*(m*n+m*n*n+n)$,不超过$70000$
欸我好像擅长将源点设成n+1将汇点设成n+2...
可以加一个不知道是不是优化的优化:中间的所有边的边权可以只设为$1$,因为这条边本身流量最大值也是$1$
(如果这条边流过,其边权变成了$0$,貌似可以使$SPFA$中该点的入队次数变少一些...只是本人猜测)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int INF = 1000000000; 4 struct edge 5 { 6 int u, v, w, c, nxt; 7 }e[70005]; 8 int n, fst[605], t[65][15], etot = 1, level[605]; 9 int q[605], pth[605]; 10 bool inq[605]; 11 12 void addedge(int u, int v, int w, int c) 13 { 14 e[++etot] = (edge){u, v, w, c, fst[u]}, fst[u] = etot; 15 e[++etot] = (edge){v, u, 0, -c, fst[v]}, fst[v] = etot; 16 } 17 18 bool SPFA() 19 { 20 int front = 0, back, u; 21 memset(level, 63, sizeof(level)); 22 level[n + 1] = 0; 23 q[back = 1] = n + 1, inq[n + 1] = true; 24 while(front != back) 25 { 26 u = q[(++front % 605)]; 27 front %= 605, inq[u] = false; 28 for(int i = fst[u]; i; i = e[i].nxt) 29 if(e[i].w && level[e[i].v] > level[u] + e[i].c) 30 { 31 level[e[i].v] = level[u] + e[i].c; 32 pth[e[i].v] = i; 33 if(!inq[e[i].v]) 34 { 35 q[(++back % 605)] = e[i].v; 36 back %= 605, inq[e[i].v] = true; 37 } 38 } 39 } 40 return level[n + 2] < INF; 41 } 42 43 int Edmond_Karp() 44 { 45 int flow = INF; 46 for(int i = pth[n + 2]; i; i = pth[e[i].u]) 47 flow = min(flow, e[i].w); 48 for(int i = pth[n + 2]; i; i = pth[e[i].u]) 49 e[i].w -= flow, e[i ^ 1].w += flow; 50 return flow * level[n + 2]; 51 } 52 53 int main() 54 { 55 int m, ptot, ans = 0; 56 scanf("%d%d", &m, &n); 57 for(int i = 1; i <= n; ++i) 58 for(int j = 1; j <= m; ++j) 59 scanf("%d", &t[i][j]); 60 for(int i = 1; i <= n; ++i) 61 addedge(i, n + 2, 1, 0); 62 ptot = n + 2; 63 for(int i = 1; i <= m; ++i) 64 for(int j = 1; j <= n; ++j) 65 { 66 ++ptot; 67 for(int k = 1; k <= n; ++k) 68 addedge(ptot, k, 1, t[k][i] * j); 69 } 70 for(int i = n + 3; i <= ptot; ++i) 71 addedge(n + 1, i, 1, 0); 72 while(SPFA()) 73 ans += Edmond_Karp(); 74 printf("%.2f\n", (double)ans / n); 75 return 0; 76 }
但是!这道题的总时限是$1s$,这种图需要大概$1.4s$,严格来说是超时的
我们有一种优化方法:
如果第$j$个人没修倒数第$i$辆车,他一定不会修倒数第$i+1$、$i+2$...辆车
所以初始的图左边只需要有$m$个点,表示每个人的倒数第$1$辆车
当图中左边点$(j, i)$到右边点$k$流了$1$时,我们再将左边的$(j, i+1)$和右边的$k$连上边(权值和之前说的一样)
这样总点数是$2+m+n+n$,不超过$131$,总边数是$2*(m+m*n+n)+2*(n+2)*n$,不超过$10000$,EK党的胜利!
你们四不四洒,就不会合作修同一辆车吗
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int INF = 1000000000; 4 struct edge 5 { 6 int u, v, w, c, nxt; 7 }e[10005]; 8 int n, fst[150], t[65][15], etot = 1, level[150]; 9 int q[150], pth[150], belong[150], fin[15], use; 10 bool inq[150]; 11 12 void addedge(int u, int v, int w, int c) 13 { 14 e[++etot] = (edge){u, v, w, c, fst[u]}, fst[u] = etot; 15 e[++etot] = (edge){v, u, 0, -c, fst[v]}, fst[v] = etot; 16 } 17 18 bool SPFA() 19 { 20 int front = 0, back, u; 21 memset(level, 63, sizeof(level)); 22 level[n + 1] = 0; 23 q[back = 1] = n + 1, inq[n + 1] = true; 24 while(front != back) 25 { 26 u = q[(++front % 150)]; 27 front %= 150, inq[u] = false; 28 for(int i = fst[u]; i; i = e[i].nxt) 29 if(e[i].w && level[e[i].v] > level[u] + e[i].c) 30 { 31 level[e[i].v] = level[u] + e[i].c; 32 pth[e[i].v] = i; 33 if(!inq[e[i].v]) 34 { 35 q[(++back % 150)] = e[i].v; 36 back %= 150, inq[e[i].v] = true; 37 } 38 } 39 } 40 return level[n + 2] < INF; 41 } 42 43 int Edmond_Karp() 44 { 45 for(int i = pth[n + 2]; i; i = pth[e[i].u]) 46 { 47 --e[i].w, ++e[i ^ 1].w; 48 if(e[i].u == n + 1) use = e[i].v; 49 } 50 return level[n + 2]; 51 } 52 53 int main() 54 { 55 int m, ptot, ans = 0, k; 56 scanf("%d%d", &m, &n); 57 for(int i = 1; i <= n; ++i) 58 for(int j = 1; j <= m; ++j) 59 scanf("%d", &t[i][j]); 60 for(int i = 1; i <= n; ++i) 61 addedge(i, n + 2, 1, 0); 62 ptot = n + 2; 63 for(int i = 1; i <= m; ++i) 64 { 65 belong[++ptot] = i, ++fin[i]; 66 for(int j = 1; j <= n; ++j) 67 addedge(ptot, j, 1, t[j][i]); 68 } 69 for(int i = n + 3; i <= ptot; ++i) 70 addedge(n + 1, i, 1, 0); 71 while(SPFA()) 72 { 73 ans += Edmond_Karp(); 74 k = belong[++ptot] = belong[use], ++fin[k]; 75 for(int i = 1; i <= n; ++i) 76 addedge(ptot, i, 1, t[i][k] * fin[k]); 77 addedge(n + 1, ptot, 1, 0); 78 } 79 printf("%.2f\n", (double)ans / n); 80 return 0; 81 }