[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

Sample Output

1.50

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$

  可以加一个不知道是不是优化的优化:中间的所有边的边权可以只设为$1$,因为这条边本身流量最大值也是$1$

  (如果这条边流过,其边权变成了$0$,貌似可以使$SPFA$中该点的入队次数变少一些...只是本人猜测)

 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党的胜利!

  你们四不四洒,就不会合作修同一辆车吗

 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 }

时间: 2024-10-18 13:53:09

[BZOJ1070] [SCOI2007] 修车 (费用流 & 动态加边)的相关文章

BZOJ 2879 美食节(费用流-动态加边)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2879 题意:有n道菜,每道菜需要b[i]份,m个厨师,第j个厨师做第i道菜需要时间a[i][j],求做完所有菜,所有人等待的最小总时间. 思路:设所有的菜为sum.一个明显的思路是将每个厨师拆成sum个点.然后sum个菜每个菜向每个厨师的每个点连边,表示该道菜为该厨师第几个做.由于这样数据太大.动态加边.每次增光一次后找到此次增广的厨师,每道菜将其连边. struct node { i

【bzoj2879】[Noi2012]美食节 费用流+动态加边

原文地址:http://www.cnblogs.com/GXZlegend 题目描述 CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节.作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴.他很快就尝遍了美食节所有的美食.然而,尝鲜的欲望是难以满足的.尽管所有的菜品都很可口,厨师做菜的速度也很快,小M仍然觉得自己桌上没有已经摆在别人餐桌上的美食是一件无法忍受的事情.于是小M开始研究起了做菜顺序的问题,即安排一个做菜的顺序使得同学们的等待时间最短.小M发现,美食节共有n种不同的菜品.每次点

bzoj2879 [Noi2012]美食节 [费用流动态加边]

Description CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节.作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴.他很快就尝遍了美食节所有的美食.然而,尝鲜的欲望是难以满足的.尽管所有的菜品都很可口,厨师做菜的速度也很快,小M仍然觉得自己桌上没有已经摆在别人餐桌上的美食是一件无法忍受的事情.于是小M开始研究起了做菜顺序的问题,即安排一个做菜的顺序使得同学们的等待时间最短.小M发现,美食节共有n种不同的菜品.每次点餐,每个同学可以选择其中的一个菜品.总共有m个厨师来制作这些菜

bzoj 1070: [SCOI2007]修车 -- 费用流

1070: [SCOI2007]修车 Time Limit: 1 Sec  Memory Limit: 128 MB Description 同一时刻有N位车主带着他们的爱车来到了汽车维修中心.维修中心共有M位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的.现在需要安排这M位技术人员所维修的车及顺序,使得顾客平均等待的时间最小. 说明:顾客的等待时间是指从他把车送至维修中心到维修完毕所用的时间. Input 第一行有两个m,n,表示技术人员数与顾客数. 接下来n行,每行m个整数.第

[BZOJ2879] [Noi2012] 美食节 (费用流 &amp; 动态加边)

Description CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节.作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴.他很快就尝遍了美食节所有的美食.然而,尝鲜的欲望是难以满足的.尽管所有的菜品都很可口,厨师做菜的速度也很快,小M仍然觉得自己桌上没有已经摆在别人餐桌上的美食是一件无法忍受的事情.于是小M开始研究起了做菜顺序的问题,即安排一个做菜的顺序使得同学们的等待时间最短.小M发现,美食节共有n种不同的菜品.每次点餐,每个同学可以选择其中的一个菜品.总共有m个厨师来制作这些菜

[NOI2012][bzoj2879] 美食节 [费用流+动态加边]

题面 传送门 思路 先看看这道题 修车 仔细理解一下,这两道题是不是一样的? 这道题的不同之处 但是有一个区别:本题中每一种车有多个需求,但是这个好办,连边的时候容量涨成$p\lbrack i\rbrack$就好了 但是还有一个区别:数据量变大了-_- 这直接导致了费用流裸做,TLE60分,因为有超过6e6条边 我们得想个办法改进一下 观察可得,这道题里,按照我们的模型,最多出现800条增广路,而且每次增广都是一的流量 也就是说我们实际上跑800次spfa即可 但是spfa和边唯一相关,我们全建

bzoj 2879: [Noi2012]美食节(费用流+动态加边)

2879: [Noi2012]美食节 Time Limit: 10 Sec Memory Limit: 512 MB Submit: 1453 Solved: 778 [Submit][Status][Discuss] Description CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节.作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴.他很快就尝遍了美食节所有的... http://bbs.chinaacc.com/forum-2-3/topic-5785721.html ht

P2053 [SCOI2007]修车 费用流

https://www.luogu.org/problemnew/show/P2053 题意 同一时刻有N位车主带着他们的爱车来到了汽车维修中心.维修中心共有M位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的.现在需要安排这M位技术人员所维修的车及顺序,使得顾客平均等待的时间最小. 说明:顾客的等待时间是指从他把车送至维修中心到维修完毕所用的时间. 思路 左边放n个点,表示n辆车.右边放m * n个点,表示m个工人,拆除n个点表示不同阶段.即如果第i辆车连上了第m个工人的第k个阶段

luogu P2053 [SCOI2007]修车 |费用流

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