[SCOI2007]修车

题目描述

同一时刻有N位车主带着他们的爱车来到了汽车维修中心。维修中心共有M位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的。现在需要安排这M位技术人员所维修的车及顺序,使得顾客平均等待的时间最小。

说明:顾客的等待时间是指从他把车送至维修中心到维修完毕所用的时间。

输入输出格式

输入格式:

 

第一行有两个数M,N,表示技术人员数与顾客数。

接下来n行,每行m个整数。第i+1行第j个数表示第j位技术人员维修第i辆车需要用的时间T。

 

输出格式:

 

最小平均等待时间,答案精确到小数点后2位。

 

输入输出样例

输入样例#1:
2 2
3 2
1 4
输出样例#1:
1.50

说明

(2<=M<=9,1<=N<=60), (1<=T<=1000)

思路:构图+费用流

直接看了题解,这种构图,我只能说:“牛逼!”

把n个工人拆成n*m个,这样每个点就表示某个时段(并非顺次对应)的工人了,然后与n个顾客连边,边权为维修用时*工人工作时段。

然后跑一个费用流,就是总用时f(其实是每个顾客等待用时之和),然后ans=f/n。

代码实现:

 1 #include<cstdio>
 2 #include<cstring>
 3 const int inf=2139062143;
 4 const int maxn=1000;
 5 const int maxm=80000;
 6 int m,n,s,t,nf,nc,tc;
 7 int a,b,c;
 8 inline int min_(int x,int y){return x<y?x:y;}
 9 int h[maxn],hs=1;
10 struct edge{int s,n,w,f;}e[maxm];
11 void add(int q,int z,int f){
12     e[++hs]=(edge){z,h[q],1,f},h[q]=hs;
13     e[++hs]=(edge){q,h[z],0,-f},h[z]=hs;
14 }
15 int w[maxn],p[maxn][2],q[maxm],head,tail;
16 long long la,lb;
17 int spfa(){
18     memset(w,0x7f,sizeof(w));
19     head=tail=0;
20     q[head++]=s,w[s]=0;
21     while(head>tail){
22         a=q[tail++];
23         for(int i=h[a];i;i=e[i].n)
24         if(e[i].w){
25             la=e[i].f,lb=w[a],la+=lb,lb=w[e[i].s];
26             if(la<lb){
27                 w[e[i].s]=la;
28                 p[e[i].s][0]=i;
29                 p[e[i].s][1]=a;
30                 q[head++]=e[i].s;
31             }
32         }
33     }
34     return w[t];
35 }
36 int ap(int k,int v){
37     if(k==s) return v;
38     int ret=ap(p[k][1],min_(e[p[k][0]].w,v));
39     e[p[k][0]].w-=ret;
40     e[p[k][0]^1].w+=ret;
41     return ret;
42 }
43 bool Dinic(){
44     nc=spfa();
45     if(nc==inf) return false;
46     nf=ap(t,inf);
47     tc+=nf*nc;
48     return true;
49 }
50 int main(){
51     freopen("scoi2007_repair.in","r",stdin);
52     freopen("scoi2007_repair.out","w",stdout);
53     scanf("%d%d",&m,&n);
54     s=0,t=n*m+n+1;
55     for(int i=1;i<=n;i++) add(s,i,0);
56     for(int i=n+1;i<=n*m+n;i++) add(i,t,0);
57     for(int i=1;i<=n;i++)
58     for(int j=1;j<=m;j++){
59         scanf("%d",&a);
60         for(int k=1;k<=n;k++) add(i,n*j+k,a*k);
61     }
62     while(Dinic());
63     printf("%.2lf\n",1.0*tc/n);
64     return 0;
65 }

aaa~反边费用忘记调成负数了。

另外,仍然建议去COGS,因为有数据。

题目来源:洛谷,COGS

posted @ 2017-03-22 15:30  J_william  阅读(207)  评论(0编辑  收藏  举报