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$中该点的入队次数变少一些...只是本人猜测)

 #include <bits/stdc++.h>
using namespace std;
const int INF = ;
struct edge
{
int u, v, w, c, nxt;
}e[];
int n, fst[], t[][], etot = , level[];
int q[], pth[];
bool inq[]; void addedge(int u, int v, int w, int c)
{
e[++etot] = (edge){u, v, w, c, fst[u]}, fst[u] = etot;
e[++etot] = (edge){v, u, , -c, fst[v]}, fst[v] = etot;
} bool SPFA()
{
int front = , back, u;
memset(level, , sizeof(level));
level[n + ] = ;
q[back = ] = n + , inq[n + ] = true;
while(front != back)
{
u = q[(++front % )];
front %= , inq[u] = false;
for(int i = fst[u]; i; i = e[i].nxt)
if(e[i].w && level[e[i].v] > level[u] + e[i].c)
{
level[e[i].v] = level[u] + e[i].c;
pth[e[i].v] = i;
if(!inq[e[i].v])
{
q[(++back % )] = e[i].v;
back %= , inq[e[i].v] = true;
}
}
}
return level[n + ] < INF;
} int Edmond_Karp()
{
int flow = INF;
for(int i = pth[n + ]; i; i = pth[e[i].u])
flow = min(flow, e[i].w);
for(int i = pth[n + ]; i; i = pth[e[i].u])
e[i].w -= flow, e[i ^ ].w += flow;
return flow * level[n + ];
} int main()
{
int m, ptot, ans = ;
scanf("%d%d", &m, &n);
for(int i = ; i <= n; ++i)
for(int j = ; j <= m; ++j)
scanf("%d", &t[i][j]);
for(int i = ; i <= n; ++i)
addedge(i, n + , , );
ptot = n + ;
for(int i = ; i <= m; ++i)
for(int j = ; j <= n; ++j)
{
++ptot;
for(int k = ; k <= n; ++k)
addedge(ptot, k, , t[k][i] * j);
}
for(int i = n + ; i <= ptot; ++i)
addedge(n + , i, , );
while(SPFA())
ans += Edmond_Karp();
printf("%.2f\n", (double)ans / n);
return ;
}

  但是!这道题的总时限是$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党的胜利!

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

 #include <bits/stdc++.h>
using namespace std;
const int INF = ;
struct edge
{
int u, v, w, c, nxt;
}e[];
int n, fst[], t[][], etot = , level[];
int q[], pth[], belong[], fin[], use;
bool inq[]; void addedge(int u, int v, int w, int c)
{
e[++etot] = (edge){u, v, w, c, fst[u]}, fst[u] = etot;
e[++etot] = (edge){v, u, , -c, fst[v]}, fst[v] = etot;
} bool SPFA()
{
int front = , back, u;
memset(level, , sizeof(level));
level[n + ] = ;
q[back = ] = n + , inq[n + ] = true;
while(front != back)
{
u = q[(++front % )];
front %= , inq[u] = false;
for(int i = fst[u]; i; i = e[i].nxt)
if(e[i].w && level[e[i].v] > level[u] + e[i].c)
{
level[e[i].v] = level[u] + e[i].c;
pth[e[i].v] = i;
if(!inq[e[i].v])
{
q[(++back % )] = e[i].v;
back %= , inq[e[i].v] = true;
}
}
}
return level[n + ] < INF;
} int Edmond_Karp()
{
for(int i = pth[n + ]; i; i = pth[e[i].u])
{
--e[i].w, ++e[i ^ ].w;
if(e[i].u == n + ) use = e[i].v;
}
return level[n + ];
} int main()
{
int m, ptot, ans = , k;
scanf("%d%d", &m, &n);
for(int i = ; i <= n; ++i)
for(int j = ; j <= m; ++j)
scanf("%d", &t[i][j]);
for(int i = ; i <= n; ++i)
addedge(i, n + , , );
ptot = n + ;
for(int i = ; i <= m; ++i)
{
belong[++ptot] = i, ++fin[i];
for(int j = ; j <= n; ++j)
addedge(ptot, j, , t[j][i]);
}
for(int i = n + ; i <= ptot; ++i)
addedge(n + , i, , );
while(SPFA())
{
ans += Edmond_Karp();
k = belong[++ptot] = belong[use], ++fin[k];
for(int i = ; i <= n; ++i)
addedge(ptot, i, , t[i][k] * fin[k]);
addedge(n + , ptot, , );
}
printf("%.2f\n", (double)ans / n);
return ;
}

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

  1. [BZOJ1070][SCOI2007]修车 费用流

    1070: [SCOI2007]修车 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 6209  Solved: 2641[Submit][Status] ...

  2. [bzoj1070][SCOI2007]修车——费用流

    题目大意: 传送门 题解: 本题和(POJ3686)[http://poj.org/problem?id=3686]一题一模一样,而且还是数据缩小以后的弱化版QAQ,<挑战程序设计竞赛>一 ...

  3. 【BZOJ1070】[SCOI2007]修车 费用流

    [BZOJ1070][SCOI2007]修车 Description 同一时刻有N位车主带着他们的爱车来到了汽车维修中心.维修中心共有M位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的. ...

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

    原文地址:http://www.cnblogs.com/GXZlegend 题目描述 CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节.作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴.他 ...

  5. bzoj 1070: [SCOI2007]修车 费用流

    1070: [SCOI2007]修车 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 2785  Solved: 1110[Submit][Status] ...

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

    Description CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节.作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴.他很快就尝遍了美食节所有的美食.然而,尝鲜的欲望是难以满足的.尽 ...

  7. P2053 [SCOI2007]修车 费用流

    $ \color{#0066ff}{ 题目描述 }$ 同一时刻有N位车主带着他们的爱车来到了汽车维修中心.维修中心共有M位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的.现在需要安排这M ...

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

    题面 传送门 思路 先看看这道题 修车 仔细理解一下,这两道题是不是一样的? 这道题的不同之处 但是有一个区别:本题中每一种车有多个需求,但是这个好办,连边的时候容量涨成$p\lbrack i\rbr ...

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

    题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2879 题意:有n道菜,每道菜需要b[i]份,m个厨师,第j个厨师做第i道菜需要时间a[i ...

随机推荐

  1. Linux系统内存占用90%以上——解决方法

    Linux系统内存占用90%以上--解决方法   首先要明确一个问题:Linux系统内存占用90%以上,是否属于正常范围?网上有详细的解释,这属于正常现象~~~    www.2cto.com   L ...

  2. 【JavaWeb】客户关系管理系统

    前言 为了巩固开发的流程,我们再拿一个客户关系管理系统来练手...! 成果图 我们完成的就是下面的项目! 搭建配置环境 配置Tomcat 导入开发包 建立开发用到的程序包 在数据库创建相对应的表 CR ...

  3. CentOS 7.3 minimal 开启网络服务

    CentOS7解决不能上网问题 1.先进入控制台 输入ip addr 2.然后su 获取超级管理员权限 3.编辑网络配置文件 vi  /etc/sysonfig/network-scripts/ifc ...

  4. java学习笔记(详细)

    java平台 1.J2SE java开发平台标准版 2.J2EE java开发平台企业版 java程序需要在虚拟机上才可以运行,换言之只要有虚拟机的系统都可以运行java程序.不同系统上要安装对应的虚 ...

  5. Java.lang.Comparable接口和Java.util.Comparator接口的区别

    Java的Comparator和Comparable当需要排序的集合或数组不是单纯的数字型时,通常可以使用Comparator或Comparable,以简单的方式实现对象排序或自定义排序. 1.Com ...

  6. HDU - 1430 魔板 (bfs预处理 + 康托)

    对于该题可以直接预处理初始状态[0, 1, 2, 3, 4, 5, 6, 7]所有可以到达的状态,保存到达的路径,直接打印答案即可. 关于此处的状态转换:假设有初始状态为2,3,4,5,0,6,7,1 ...

  7. Python+Selenium基础篇之1-环境搭建

    Python + Selenium 自动化环境搭建过程 1. 所需组建 1.1 Selenium for python 1.2 Python 1.3 Notepad++ 作为刚初学者,这里不建议使用P ...

  8. Linux shell的问题

    1.uptime命令可以查看当前系统的启动时间: w命令显示当前登录者top命令显示当前任务ps命令显示所有进程信息 uptime命令可以查看系统启动时间   2.使用shell时,默认的环境变量放在 ...

  9. java-数据库连接,分层实现增删改查测试

    成员属性类: public class Dog { private int number; private String name; private String strain; private St ...

  10. 远程控制你的智能电视,按键|输入|安装App等都已实现,已开源!

    一.序 Hi,大家好,我是承香墨影! 智能电视或者智能盒子,不知道大家了解多少? 这两年各大厂商生产的电视设备,基本上都是搭载的 Android 系统.既然电视本身就是 Android 系统的,我们也 ...