[SCOI2007]修车

题面

<center> [SCOI2007]修车<center>
<center>时间限制:1秒 内存限制:128MB<center>

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

2 2
3 2

1 4

样例输出

1.50

提示
数据范围: (2<=M<=9,1<=N<=60)

思路

题解
  第一感觉,是用DP做,用DP想了半天发觉不对。搜了题解才知道,是用最大流的最小费用做。 地地道道的一道模板题,在网上搜到了模板。最主要的是:如何建图。首先建立源点S和汇点T,N辆车和M个工人都作为中间的节点,其中每位工人分为N个,代表服务车的顺序。将S与N辆车相连,N*M个工人节点与T相连,流均为1,费用为0,再将N辆车与N*M个工人节点相连,流也为1,费用计算方法为:k*ti,即第i辆车由第j个工人修理的时间*后面需要的车辆数(意思是:假如第j个工人修理第i辆车的次序为x,那么还有(n-x)辆车也需要等待ti的时间)。建图完毕后,调用模板进行计算即可得出总的等待时间,除以n就可以平均等待时间。

源码

#include <iostream>
#include <queue>
#include <cstdio>
#include <cstring>
#define inf 0x7fffffff
#define mem(a,b) memset(a,b,sizeof(a))
#define T 1001
using namespace std;
const int MAXN=1e6+5;
int n,m,ans,cnt=0,st,ed,t[65][10];
int head,tail;
int list[1005],pre[1005],vi[1005],first[1005],c[1005];
bool v[1005];
typedef struct {//定义节点类型
    int from,to,next,f,w,other;
}Node;
Node e[MAXN];
void insert(int u,int v,int f,int w){//插入节点(包括正向节点和反向节点)
    int kx,ky;
    cnt++;
    kx=cnt;
    e[cnt].from=u;e[cnt].to=v;e[cnt].f=f;e[cnt].w=w;
    e[cnt].next=first[u];first[u]=cnt;
    cnt++;ky=cnt;
    e[cnt].from=v;e[cnt].to=u;e[cnt].f=0;e[cnt].w=-w;
    e[cnt].next=first[v];first[v]=cnt;
    e[kx].other=ky;e[ky].other=kx;
}

bool spfa(){//求解算法
    mem(c,0);c[st]=999999999;
    //memset(v,false,sizeof(v));
    mem(v,false);
    v[st]=true;
    mem(vi,0x3F);vi[st]=0;
    list[1]=st;head=1;tail=2;pre[st]=0;
    while(head!=tail){
        int x=list[head];
        for(int k=first[x];k>0;k=e[k].next){
            int y=e[k].to;
            if(e[k].f>0&&vi[y]>vi[x]+e[k].w){
                vi[y]=vi[x]+e[k].w;
                c[y]=min(c[x],e[k].f);
                pre[y]=k;
                if(v[y]==false){
                    v[y]=true;
                    list[tail++]=y;
                    if(tail==ed+1)tail=1;
                }
            }
        }
        head++;if(head==ed+1)head=1;
        v[x]=false;
    }
    if(vi[ed]>999999999)return false;
    ans+=vi[ed]*c[ed];
    int x=ed;
    while(x!=st){
        int k=pre[x];
        e[k].f-=c[ed];e[e[k].other].f+=c[ed];
        x=e[k].from;
    }
    return true;
}
int main(){
    scanf("%d%d",&m,&n);
    st=n*m+n+1;ed=n*m+n+2;
    cnt=0;mem(first,0);
    int i,j,k,p;
    for(i=1;i<=n;i++)
        for( j=1;j<=m;j++){
            scanf("%d",&t[i][j]);//输入第i辆车由第j个工人修理所需时间
        }
    for(i=1;i<=n;i++) insert(st,i,1,0);//将源点S和N个车节点相连
    p=m*n;
    for(j=1;j<=p;j++) insert(n+j,ed,1,0);//将N*M个工人节点与汇点T相连
    for(i=1;i<=n;i++){
        for(j=1;j<=m;j++){
            for(k=1;k<=n;k++){
                insert(i,j*n+k,1,t[i][j]*(n-k+1));//将N个车节点与N*M个工人节点相连
            }
        }
    }
    ans=0;

    while(spfa());//求解

    printf("%.2lf\n",double(ans)/double(n));
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值