zoukankan      html  css  js  c++  java
  • 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 }
    点我点我
  • 相关阅读:
    vue 中的const {XXX } =this 的作用效果
    <a href="javascript:;">的用法说明
    iOS抓包工具Charles —— 破解、抓包入门
    iOS抓包工具Charles
    Android抓包方法(一)之Fiddler代理
    appium+python自动化24-滑动方法封装(swipe)
    Appium移动自动化测试(五)--app控件获取之uiautomatorviewer
    appium自动化框架项目实战1——app初次使用引导页
    一个完整的Appium手机自动化测试实例
    Appium提高脚本复用、可配置性
  • 原文地址:https://www.cnblogs.com/liuzhanshan/p/7873313.html
Copyright © 2011-2022 走看看