bzoj1070(最小费用最大流)

12 篇文章 0 订阅

1070: [SCOI2007]修车

Time Limit: 1 Sec   Memory Limit: 162 MB
Submit: 3474   Solved: 1388
[ Submit][ Status][ Discuss]

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


解题思路:

把每个工人拆成N个点。记为A[i,j]表示第i个工人修倒数第j辆车。

每个车跟所有N*M个工人拆出的点连边。流量为1,费用为time[i,j]*k。

源和每辆车连边,N*M个点和汇连边,流量都为1,费用同为0。

 

为什么这么构图呢?

 

考虑第i个工人,他修第j辆车只对后面要修的车有影响,而前面修过的车已经对当前没有影响了。

而这个影响就是后面每个将要修理的车都多等待了time的时间。

 

其他边流量都为1是显然的,每辆车修一次,每个工人一个时段只能修理一辆车。


注意:bzoj上的double的输出格式要严格"%lf"


#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
int n,m,T,ans=0;
int x[10][61];
int len=1;
struct data
 {
  int from,to,v,c,next;
 }e[200000];
int h[1005];
int dis[1005];
int q[1005];
bool bb[1005];
int pre[1005];


void insert(int x,int y,int v,int c)
 {
  ++len; e[len].from=x; e[len].to=y;
  e[len].v=v; e[len].c=c; e[len].next=h[x]; h[x]=len;
 }


bool spfa()
 {
    memset(bb,true,sizeof(bb));
    memset(dis,0x7f,sizeof(dis));
    int tail=1; int head=0; bb[0]=false; q[tail]=0; dis[0]=0; pre[0]=-1;
    while (head!=tail)
     {
        head=(head+1)%1001;
        int u=h[q[head]];
        while (u!=0)
         {
            if (e[u].v && dis[q[head]]+e[u].c<dis[e[u].to])
             {
                dis[e[u].to]=dis[q[head]]+e[u].c;
                pre[e[u].to]=u;
                if (bb[e[u].to])
                 {
                    bb[e[u].to]=false;
                    tail=(tail+1)%1001;
                    q[tail]=e[u].to;
                 }
             }
            u=e[u].next;
         }
        bb[q[head]]=true;
      }
    if (dis[T]<1000000) return 1;else
     return 0;
 }
 
void dfs()
 {
    int uu=0x7fffffff;
    int now=T; 
    while (pre[now]!=-1)
     {
        uu=min(e[pre[now]].v,uu);
        now=e[pre[now]].from;
      }
    now=T;
    while (pre[now]!=-1)
     {
      ans+=uu*e[pre[now]].c;
        e[pre[now]].v-=uu; e[pre[now]^1].v+=uu;
        now=e[pre[now]].from;
     }
    
 }


int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;++i)
for (int j=1;j<=n;++j)
 {
  scanf("%d",&x[j][i]);
 }
for (int i=1;i<=n*m;++i)
 {
   insert(0,i,1,0); insert(i,0,0,0);
 }
T=1001;
for (int i=1;i<=m;++i)
{
insert(n*m+i,T,1,0); insert(T,n*m+i,0,0);
}
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j)
 for (int k=1;k<=m;++k)
  {
  insert((i-1)*m+j,n*m+k,1,x[i][k]*j);
  insert(n*m+k,(i-1)*m+j,0,-x[i][k]*j);
  }
while (spfa()){dfs();}
double sss=double(ans)/m;
printf("%.2lf",sss);  

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值