BZOJ1070[SCOI2007] 修车

原题链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1070

修车

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)

题解

假设一个人修了 k k 辆车,维修这k辆车所需要的时间分别为 t1,t2,,tk t 1 , t 2 , ⋯ , t k ,那么顾客总共等待的时间为:

t1+(t1+t2)+(t1+t2+t3)++(t1+t2++tk)=k×t1+(k1)×t2++2×tk1+tk=i=1k(ki+1)ti t 1 + ( t 1 + t 2 ) + ( t 1 + t 2 + t 3 ) + ⋯ + ( t 1 + t 2 + ⋯ + t k ) = k × t 1 + ( k − 1 ) × t 2 + ⋯ + 2 × t k − 1 + t k = ∑ i = 1 k ( k − i + 1 ) t i

反着思考一下,倒数第 i i 个修的车对等待时间的贡献为i×t

那么我们不如将每个维修员拆成 n n 个点,分别对应修的倒数第n个车,代表车的点向 n×m n × m 个点依次连边,容量为 1 1 ,费用为i×t

代码
#include<bits/stdc++.h>
using namespace std;
const int M=1e4+5;
struct sd{int to,fl,val;}ed[M*30];
int head[M],nxt[M*30],num[M*30],dis[M],minf[M],pre[M],car[65][65],n,m,s,t,id,cnt,ans,mx;
bool vis[M];
queue<int>dui;
void added(int f,int t,int w,int v){nxt[++cnt]=head[f],head[f]=cnt,num[cnt]=id;ed[id++]=(sd){t,w,v};}
void add(int f,int t,int w,int v){added(f,t,w,v),added(t,f,0,-v);}
bool SPFA(int s,int t)
{
    memset(dis,127,sizeof(dis));memset(vis,0,sizeof(vis));
    dui.push(s);dis[s]=0,vis[s]=1,minf[s]=0x3f3f3f3f;
    int f;sd hh;
    while(!dui.empty())
    {
        f=dui.front();dui.pop();
        vis[f]=0;
        for(int i=head[f];i;i=nxt[i])
        {
            hh=ed[num[i]];
            if(hh.fl>0&&dis[hh.to]>dis[f]+hh.val)
            {
                dis[hh.to]=dis[f]+hh.val;
                minf[hh.to]=min(minf[f],hh.fl);
                pre[hh.to]=num[i];
                if(!vis[hh.to])dui.push(hh.to),vis[hh.to]=1;
            }
        }
    }
    return dis[t]<2e9;
}
void up(int s,int t)
{
    int v=t,hh;
    while(v!=s)hh=pre[v],ed[hh].fl-=minf[t],ed[hh^1].fl+=minf[t],v=ed[hh^1].to;
    ans+=minf[t]*dis[t];
}
void in()
{
    int a;scanf("%d%d",&m,&n);t=(mx=n*m)+n+1;
    for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)scanf("%d",&car[i][j]);
    for(int i=1;i<=n;++i)add(s,mx+i,1,0);for(int i=1;i<=mx;++i)add(i,t,1,0);
    for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)for(int k=1;k<=n;++k)add(mx+i,(j-1)*n+k,1,k*car[i][j]);
}
void ac()
{
    while(SPFA(s,t))up(s,t);
    printf("%.2lf\n",1.0*ans/n);
}
int main(){in();ac();}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值