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

网络流的精彩之处就在于建图,这道题的建图方法就特别好玩。

首先,题目要求的是最小的顾客平均等待的时间,由于总的汽车数一定是n,所以我们只需要求出最小的总的等待时间就可以了。

由于每个人同一时间只能修1辆,每辆车修一次就够了(流量限制),用m个人修n次即可(流量守恒),有网络流的性质,而且求极值,所以想到用最小费用最大流解决。

但还有一个问题,边的权值只有一个,那如何合并时间(次序)与修车费用这两个维度呢?

由于可以同时工作,我们分别考虑每个人。假定 i 辆车是某人 倒数第 j 个修的,那么这辆车对总的等待时间的贡献就是 修理i的费用×j(修理时,有 j-1 辆车在等待其修理完成)

所以我们这样建图:

每个人i拆成多个点,表示这个人 倒数第k次修理车(k最大为n),对某个单点,与表示车辆j的点添加一条流量为1,权值为 k×cost[i][j]

为保证每辆车只修理一次,从表示车辆的点向汇点连一条流量为1的边即可

代码:

  1 /*
  2  * @FileName: /media/shan/Study/代码与算法/OJ(无法分类的题目)/BZOJ/1070/bzoj1070.cpp
  3  * @Author: Pic
  4  * @Created Time: 2017年11月21日 星期二 13时51分31秒
  5  */
  6 #include <bits/stdc++.h>
  7 using namespace std;
  8 const int maxn=1000;
  9 const int INF=0x3f3f3f3f;
 10 struct Edge
 11 {
 12     int from,to,cap,flow,cost;
 13     Edge(){}
 14     Edge(int f,int t,int c,int fl,int co):from(f),to(t),cap(c),flow(fl),cost(co){}
 15 };
 16 struct MCMF
 17 {
 18     int n,m,s,t;
 19     vector<Edge> edges;
 20     vector<int> G[maxn];
 21     bool inq[maxn];     //是否在队列
 22     int d[maxn];        //Bellman_ford单源最短路径
 23     int p[maxn];        //p[i]表从s到i的最小费用路径上的最后一条弧编号
 24     int a[maxn];        //a[i]表示从s到i的最小残量
 25 
 26     //初始化
 27     void init(int n,int s,int t)
 28     {
 29         this->n=n, this->s=s, this->t=t;
 30         edges.clear();
 31         for(int i=0;i<n;++i) G[i].clear();
 32     }
 33 
 34     //添加一条有向边
 35     void AddEdge(int from,int to,int cap,int cost)
 36     {
 37         edges.push_back(Edge(from,to,cap,0,cost));
 38         edges.push_back(Edge(to,from,0,0,-cost));
 39         m=edges.size();
 40         G[from].push_back(m-2);
 41         G[to].push_back(m-1);
 42     }
 43 
 44     //求一次增广路
 45     bool BellmanFord(int &flow, int &cost)
 46     {
 47         for(int i=0;i<n;++i) d[i]=INF;
 48         memset(inq,0,sizeof(inq));
 49         d[s]=0, a[s]=INF, inq[s]=true, p[s]=0;
 50         queue<int> Q;
 51         Q.push(s);
 52         while(!Q.empty())
 53         {
 54             int u=Q.front(); Q.pop();
 55             inq[u]=false;
 56             int len=G[u].size();
 57             for(int i=0;i<len;++i)
 58             {
 59                 Edge &e=edges[G[u][i]];
 60                 if(e.cap>e.flow && d[e.to]>d[u]+e.cost)
 61                 {
 62                     d[e.to]= d[u]+e.cost;
 63                     p[e.to]=G[u][i];
 64                     a[e.to]= min(a[u],e.cap-e.flow);
 65                     if(!inq[e.to]){ Q.push(e.to); inq[e.to]=true; }
 66                 }
 67             }
 68         }
 69         if(d[t]==INF) return false;
 70         flow +=a[t];
 71         cost +=a[t]*d[t];
 72         int u=t;
 73         while(u!=s)
 74         {
 75             edges[p[u]].flow += a[t];
 76             edges[p[u]^1].flow -=a[t];
 77             u = edges[p[u]].from;
 78         }
 79         return true;
 80     }
 81 
 82     //求出最小费用最大流
 83     int Min_cost()
 84     {
 85         int flow=0,cost=0;
 86         while(BellmanFord(flow,cost));
 87         return cost;
 88     }
 89 }MM;
 90 int c[maxn][maxn];
 91 int main()
 92 {
 93     //freopen("data.in","r",stdin);
 94     //freopen("data.out","w",stdout);
 95     int m,n;
 96     while(~scanf("%d%d",&m,&n)){
 97         int s=n*m+n+1;
 98         int t=s+1;
 99         MM.init(t+1,s,t);
100         for(int i=0;i<n;i++){
101             for(int j=0;j<m;j++){
102                 scanf("%d",&c[i][j]);
103             }
104         }
105         for(int i=0;i<m;i++){
106             for(int j=0;j<n;j++){
107                 for(int k=0;k<n;k++){
108                     MM.AddEdge(i*n+j,n*m+k,1,c[k][i]*(n-j));
109                 }
110             }
111         }
112         for(int i=0;i<n*m;i++){
113             MM.AddEdge(s,i,1,0);
114         }
115         for(int i=n*m;i<n*m+n;i++){
116             MM.AddEdge(i,t,1,0);
117         }
118         int res=MM.Min_cost();
119         printf("%.2lf
",res*1.0/n);
120     }
121 
122     return 0;
123 }
点我点我
原文地址:https://www.cnblogs.com/liuzhanshan/p/7873313.html