bzoj1070【SCOI2007】修车(费用流)

题目描述

同一时刻有N位车主带着他们的爱车来到了汽车维修中心。维修中心共有M位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的。现在需要安排这M位技术人员所维修的车及顺序,使得顾客平均等待的时间最小。

说明:顾客的等待时间是指从他把车送至维修中心到维修完毕所用的时间。

输入输出格式

输入格式:

第一行有两个数M,N,表示技术人员数与顾客数。

接下来n行,每行m个整数。第i+1行第j个数表示第j位技术人员维修第i辆车需要用的时间T。

输出格式:

最小平均等待时间,答案精确到小数点后2位。

输入输出样例

输入样例#1: 复制

2 2
3 2
1 4

输出样例#1: 复制

1.50

说明

(2<=M<=9,1<=N<=60), (1<=T<=1000)

题解

  这篇还是写得详细一点好了……因为不是很懂……

  我们考虑一下,如果一个工人修车的序列为$W_1,W_2,W_3...W_n$

  那么对于这几辆车的车主而言,他们等待的总时间是$\sum _{i=1}^n W_i*(n-i+1)=nW_1+(n-1)W_2+...+W_n$(因为一个人在越前面修,会使后面更多的人要等待他的车修好)

  然后因为平均时间最少,人数是不变的,所以得保证总时间最少

  我们发现,如果把第$i$个人的车让第$j$个人在倒数第$k$个修(以下表示为$(i,j,k)$),那么对总时间的贡献是$T(i,j)*k$,其中$T(i,j)$表示第$j$个人修第$i$辆车的时间

  然后因为每一辆车只能被一个人修,每一个人同一时间只能修一辆车

  那么我们可以把$(j,k)$表示成一个状态,表示被第$j$个人在倒数第$k$个修,那么不难发现每一个状态只能被匹配一次,即不可能有两辆车同时被一个人在同一个顺序修

  那么我们可以建一个二分图,左边是$n$辆车,右边是$n*m$个状态$(j,k)$(因为$k$不可能超过$n$),然后左边的每一个点向右边所有点连边,容量为$1$,费用为对应的$(i,j,k)$

  然后因为每一辆车只会被修一次,所以从源点向所有车连容$1$费$0$的边

  因为每一个人在同一时间只能修一辆车,所以右边所有状态向汇点连容$1$费$0$的边

  当网络跑满的时候说明所有车都有人修了,然后又要时间最少,只要在此基础上求一个最小费用流即可

 1 //minamoto
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<queue>
 5 #include<cstring>
 6 #define inf 0x3f3f3f3f
 7 #define id(i,j) (i-1)*n+j
 8 using namespace std;
 9 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
10 char buf[1<<21],*p1=buf,*p2=buf;
11 inline int read(){
12     #define num ch-‘0‘
13     char ch;bool flag=0;int res;
14     while(!isdigit(ch=getc()))
15     (ch==‘-‘)&&(flag=true);
16     for(res=num;isdigit(ch=getc());res=res*10+num);
17     (flag)&&(res=-res);
18     #undef num
19     return res;
20 }
21 const int N=1005,M=100005;
22 int ver[M],Next[M],head[N],edge[M],flow[M],tot=1;
23 int vis[N],dis[N],disf[N],Pre[N],last[N],n,m,s,t;
24 queue<int> q;
25 inline void add(int u,int v,int f,int e){
26     ver[++tot]=v,Next[tot]=head[u],head[u]=tot,flow[tot]=f,edge[tot]=e;
27     ver[++tot]=u,Next[tot]=head[v],head[v]=tot,flow[tot]=0,edge[tot]=-e;
28 }
29 bool spfa(){
30     memset(dis,0x3f,sizeof(dis));
31     while(!q.empty()) q.pop();
32     q.push(s),dis[s]=0,disf[s]=inf,Pre[t]=-1;
33     while(!q.empty()){
34         int u=q.front();q.pop(),vis[u]=0;
35         for(int i=head[u];i;i=Next[i]){
36             int v=ver[i];
37             if(flow[i]&&dis[v]>dis[u]+edge[i]){
38                 dis[v]=dis[u]+edge[i],Pre[v]=u,last[v]=i;
39                 disf[v]=min(disf[u],flow[i]);
40                 if(!vis[v]) vis[v]=1,q.push(v);
41             }
42         }
43     }
44     return ~Pre[t];
45 }
46 int dinic(){
47     int mincost=0;
48     while(spfa()){
49         int u=t;mincost+=disf[t]*dis[t];
50         while(u!=s){
51             flow[last[u]]-=disf[t],flow[last[u]^1]+=disf[t];
52             u=Pre[u];
53         }
54     }
55     return mincost;
56 }
57 int main(){
58     m=read(),n=read();
59     s=0,t=n*m+n+1;
60     for(int i=1;i<=n;++i) add(s,i,1,0);
61     for(int i=1;i<=m;++i)
62     for(int j=1;j<=n;++j)
63     add(n+id(i,j),t,1,0);
64     for(int i=1;i<=n;++i)
65     for(int j=1;j<=m;++j){
66         int cost=read();
67         for(int k=1;k<=n;++k){
68             add(i,n+id(j,k),1,cost*k);
69         }
70     }
71     printf("%.2lf",(double)dinic()/n);
72     return 0;
73 }

原文地址:https://www.cnblogs.com/bztMinamoto/p/9510697.html

时间: 2024-10-17 07:36:40

bzoj1070【SCOI2007】修车(费用流)的相关文章

[BZOJ1070] [SCOI2007] 修车 (费用流 &amp; 动态加边)

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

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

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

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

[bzoj1070][SCOI2007]修车_费用流

修车 bzoj-1070 SCOI-2007 题目大意:有m个人要修n台车,每个工人修不同的车的时间不同,问将所有的车都修完,最少需要花费的时间. 注释:$2\le m\le 9$,$1\le n \le 60$ 想法:想起了那句话...(如果题面复杂,dp状态不可描述,一看数据范围发现才几百,那八成是网络流了),一般这句话都是对的,除了那次在CF上遇到的爆搜.. ... 关于这道题,网上有题解在边上做技巧(完全没明白这样的意义),我的做法时拆点.将每个点拆成n个,然后在这些被拆的点与车之间连边

BZOJ SCOI 2007 修车 费用流

题目大意:有一些车和一些修车的人,给出每个人修每个车的时间,问所有人等待的最短平均时间是多少. 思路:记得POJ有一个和这个很像的题,做法是一样的.对于每个人修车的时候,我们只考虑他修车的时间对在它之后修车的人的时间的影响,因此我们只要考虑每一辆车是倒数第几个修的就可以了,然后朴素的建图,跑朴素的费用流,就可以过. CODE: #include <queue> #include <cstdio> #include <cstring> #include <ioman

Bzoj1070 [SCOI2007]修车

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

BZOJ 1070 修车(费用流)

如果能想到费用流,这道题就是显然了. 要求所有人的等待平均时间最小,也就是所有人的总等待时间最小. 每辆车只需要修一次,所以s连每辆车容量为1,费用为0的边. 现在需要把每个人拆成n个点,把车和每个人的第k个点连一条容量为1,费用为cost[i][j]*k的边. 最后把每个人拆完后的点向汇点连一条容量为1,费用为0的边. #include<iostream> #include<cstdio> #include<cstring> #define inf 0x7ffffff

[BZOJ1070][SCOI2007]修车(最小费用最大流)

题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1070 分析: 把每个工人拆成N个点.记为A[i,j]表示第i个工人修倒数第j辆车. 每个车跟所有N*M个工人拆出的点连边.流量为1,费用为time[i,j]*k. 源和每辆车连边,N*M个点和汇连边,流量都为1,费用同为0. 为什么这么建呢?因为如果某个工人修了某辆车,那么只会对这个工人以后修的车的时间有影响,对已经修完的没影响,于是可以倒过来考虑.

[bzoj1070][SCOI2007]修车[ 网络流]

把每个工人拆成N个点.记为A[i,j]表示第i个工人修倒数第j辆车.每个车跟所有N*M个工人拆出的点连边.流量为1,费用为$time[i,j]*k$.源和每辆车连边,N*M个点和汇连边,流量都为1,费用同为0. 1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstdlib> 5 #include <cstring> 6 #include &