• [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$

      欸我好像擅长将源点设成n+1将汇点设成n+2...

      可以加一个不知道是不是优化的优化:中间的所有边的边权可以只设为$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
    ", (double)ans / n);
    75     return 0;
    76 }
    View Code

      但是!这道题的总时限是$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
    ", (double)ans / n);
    80     return 0;
    81 }
    View Code
  • 相关阅读:
    Android 中 Fragment 的切换(解决 replace 的低效)
    Android 中 OkGo 的使用 (封装 OkHttp)
    fastjson 封装工具类
    给系统添加右键使用 IDEA 打开的功能
    发现了一个很好看的博客园主题
    AndroidStudio中如何创建指定布局的layout文件
    转载:十个前端UI优秀框架
    win10 添加 telnet 工具
    tomcat各版本与jdk及servlet各版本对应关系
    servlet和jsp的maven依赖
  • 原文地址:https://www.cnblogs.com/CtrlCV/p/5613809.html
Copyright © 2020-2023  润新知