【bzoj 1070】【codevs 2436】[SCOI2007]修车(费用流)

49 篇文章 0 订阅
47 篇文章 0 订阅

Time Limit: 1 Sec Memory Limit: 162 MB Submit: 4034 Solved: 1645
[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
HINT
数据范围:
(2<=M<=9,1<=N<=60), (1<=T<=1000)

【网络流神题,烧脑的建图】
【刚开始想要源点向每个人连边,每个人向每辆车连边,每辆车向汇点连边,车之间再互相连边,然后维护一个类似前缀和的东西,然而发现这样会出现环,而且因为费用流要跑SPFA,所以维护前缀和也会出问题; 后来又想到源点向每个人连边,每个人向每辆车连边,每辆车向汇点连边,然后求最小费用,但发现每个车所用时间最短的方案不一定满足题目要求;最后想到要将每辆车拆成m个来搞,然后搞着搞着搞成了一坨……233,欲哭无泪的窝在耗掉了一个多小时后,果断看题解】
【看到题解的那一刻有一种亮瞎眼的赶脚。标解是拆了人,将每个修理工拆成n个(表示当前修理工倒数第几个修理这台车),连边的时候源点向每辆车连边,流量为1,费用为零, 每辆车向拆出的所有人连边,这条边代表倒数第几个修当前车,流量为1,费用为倒数第几个乘修车时间(因为修当前车所花费的时间,只会影响之后修的车所花的时间,即有几人要等待这一段时间), 所有拆出的人向汇点连边,流量为1,费用为0】

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int a[100010],next[100010],p[5010],remain[100010],cost[100010],tot;
int dis[100010],last[100010];
int n,m,S,T,maxflow,mincost;
bool b[100010];

inline void add(int x,int y,int flow,int val)
{
    tot++; a[tot]=y; next[tot]=p[x]; p[x]=tot; remain[tot]=flow; cost[tot]=val;
    tot++; a[tot]=x; next[tot]=p[y]; p[y]=tot; remain[tot]=0; cost[tot]=-val;
}
inline int addflow(int s,int t)
{
    int now=t,ans=0x7fffffff;
    while (now!=s)
     {
        ans=min(ans,remain[last[now]]);
        now=a[last[now]^1];
     }
      now=t;
     while (now!=s)
     {
        remain[last[now]]-=ans;
        remain[last[now]^1]+=ans;
        now=a[last[now]^1];
     }
    return ans;
}
inline bool spfa(int s,int t)
 {
    int k;
    queue<int>que;
    memset(dis,127/3,sizeof(dis));
    memset(b,true,sizeof(b));
    k=dis[0]; dis[s]=0; que.push(s); b[s]=false; 
    while (!que.empty())
     {
        int u,v;
        u=que.front(); que.pop();
        v=p[u]; b[u]=true;
        while (v!=-1)
         {
                  if (remain[v]&&dis[a[v]]>dis[u]+cost[v])
                 {
                    dis[a[v]]=dis[u]+cost[v];
                    last[a[v]]=v;
                    if (b[a[v]])
                     {
                        b[a[v]]=false;
                        que.push(a[v]);
                }
               }
            v=next[v];
          }
     }
    if (dis[t]>=k) return false;
    int sum=addflow(s,t);
    maxflow+=sum;
    mincost+=dis[t]*sum;
    return true;
}

int main()
 {
    int i,j,k;
    memset(next,-1,sizeof(next));
    memset(p,-1,sizeof(p));
    scanf("%d%d",&m,&n);
    T=n*m+n+1; tot=-1;
    for (i=1;i<=n;++i)
     {
        add(S,i,1,0);
        for (j=1;j<=m;++j)
         {
            int x;
            scanf("%d",&x);
            for (k=1;k<=n;++k)
                         add(i,j*n+k,1,k*x);
              }
      }
   for (i=n+1;i<T;++i) add(i,T,1,0);
  while(spfa(S,T));
  printf("%.2lf\n",(double)(mincost*1.0)/n);
 return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值