zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    Android四大组件--事务详解(转)
    Android课程---关于数据存储的学习(3)之数据库和事务
    初学DW资料——js的prompt的返回值
    初学DW资料——target=的理解
    初学JAVA资料——链表
    初学JAVA资料——哈希表
    初学JAVA资料——线程
    初学JAVA——代码练习(验证字符串结束字符)
    初学JAVA——代码练习(数学运算)
    初学JAVA——代码练习(字符串)
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/9510697.html
Copyright © 2011-2022 走看看