[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)


题面在钦定费用流……
然后……然后咱就发现费用流差不多忘光了……

(╯‵□′)╯︵┻━┻


思路:
钦定费用流……
然而,咱发现时间要累加。
而费用流,至少裸费用流做不到……

考虑计算答案,每选一个车主意味着剩下没走的车主都要多等当前车主修车所需时间。

所以,观察数据范围,得到一个新的建图思路:
对于每个工人,拆成n个点,代表这位工人修倒数第1~n辆车。
对每辆车向每个工人的每个时段连边,花费为维修时间*当前时段。
然后,咱就得到了一张可以用的图~
为什么这么建图呢?
因为,这么建的意义就是你每选择一辆车给工人,就会发生让剩下所有正翘首以待的车主全体都多等上当前时间,即当前时间乘上剩余所有还在等的车主个数。
然后,所有的边流量都为1,代表选过或没选过。

然后就是费用流的锅了~ヾ(o◕∀◕)ノヾ~

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>

using namespace std;

inline int read()
{
    int x=0;char ch=getchar();
    while(ch<'0' || '9'<ch)ch=getchar();
    while('0'<=ch && ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x;
}

const int N=100023,M=1020;
const int ed=1001,Inf=0x3f3f3f;

int t[105][105],n,m,ans;
int to[N],nxt[N],from[N],cap[N],val[N],tot=1;
int dis[M],beg[M],q[M],fa[M];
bool inq[M];

inline void add(int u,int v,int w,int cost)
{
    to[++tot]=v;
    from[tot]=u;
    nxt[tot]=beg[u];
    val[tot]=cost;
    cap[tot]=w;
    beg[u]=tot;
}

inline void adde(int u,int v,int w,int cost)
{
    add(u,v,w,cost);
    add(v,u,0,-cost);
}

bool spfa()
{
    for(int i=0;i<=ed;i++)
        dis[i]=Inf;

    int l=0,r=1;
    dis[0]=0;
    inq[0]=1;
    q[0]=0;

    while(l!=r)
    {
        int u=q[l++];
        if(l==ed)l=0;
        inq[u]=0;

        for(int i=beg[u],v;i;i=nxt[i])
            if(cap[i] && dis[v=to[i]]>dis[u]+val[i])
            {
                dis[v]=dis[u]+val[i];
                fa[v]=i;
                if(!inq[v])
                {
                    inq[v]=1;
                    q[r++]=v;
                    if(r==ed)r=0;
                }
            }
    }

    if(dis[ed]==Inf)return 0;
    return 1;
}

inline int minn(int a,int b){if(a>b)return b;return a;}

void mcmf()
{
    int tmp=Inf;
    for(int i=fa[ed];i;i=fa[from[i]])
        tmp=minn(tmp,cap[i]);
    for(int i=fa[ed];i;i=fa[from[i]])
    {
        cap[i]-=tmp;
        cap[i^1]+=tmp;
        ans+=val[i]*tmp;
    }
}

int main()
{
    n=read();m=read();
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
            t[i][j]=read();

    for(int i=1;i<=n*m;i++)
        adde(0,i,1,0);
    for(int i=n*m+1;i<=n*m+m;i++)
        adde(i,ed,1,0);

    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            for(int k=1;k<=m;k++)
                adde((i-1)*m+j,n*m+k,1,t[k][i]*j);

    while(spfa())
        mcmf();

    printf("%.2lf\n",(double)ans/m);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值