[SCOI2007]修车(费用流)

[SCOI2007]修车

顾客平均等待的最小时间$=$等待总时间$/n$

考虑只有1个技术人员时,$n$辆车等待总时间

$A_1+(A_1+A_2)+(A_1+A_2+A_3)+...+\sum_{i=1}^{n}A_i$

发现第$k$个修第$i$辆车的代价为$A_i*(n-k+1)$

为了方便我们把它换个形式

倒着第$k$个修第$i$辆车的代价为$A_i*k$

想到可以用费用流解决

于是我们可以把 第$i$个人倒着第$k$个修第$j$辆车 当成一个点

车代表的点向该点连一条边 流量$=1$,费用$=A_{i,j}*k$

该点再向虚拟汇点T连一条边 流量$=1$,费用$=0$

最后再从虚拟源点$S$向每辆车连一条边 流量$=1$,费用$=0$

蓝后就可以愉快地跑费用流辣

注意输入时顺序为$m,n$

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define N 100005
int n,m,S,T,d[N],a[N],p[N],tot;
bool inh[N];
queue <int> h;
int cnt=1,hd[N],nxt[N],ed[N],poi[N],val[N],cost[N];
inline void adde(int x,int y,int v1,int v2){
    nxt[ed[x]]=++cnt, hd[x]=hd[x]?hd[x]:cnt,
    ed[x]=cnt, poi[cnt]=y, val[cnt]=v1, cost[cnt]=v2;
}
inline void link(int x,int y,int v1,int v2){adde(x,y,v1,v2),adde(y,x,0,-v2);}
bool bfs(){//裸的费用流
    memset(d,63,sizeof(d)); int inf=d[0];
    h.push(S); d[S]=0; a[S]=inf; inh[S]=1;
    while(!h.empty()){
        int x=h.front(); h.pop(); inh[x]=0;
        for(int i=hd[x];i;i=nxt[i]){
            int to=poi[i];
            if(val[i]&&d[to]>d[x]+cost[i]){
                d[to]=d[x]+cost[i]; p[to]=i;
                a[to]=min(a[x],val[i]);
                if(!inh[to]) h.push(to),inh[to]=1;
            }
        }
    }if(d[T]==inf) return 0;
    tot+=a[T]*d[T];
    for(int i=T;i!=S;i=poi[p[i]^1])
        val[p[i]]-=a[T],val[p[i]^1]+=a[T];
    return 1;
}
int main(){
    scanf("%d%d",&m,&n); int w=n*m,q;
    S=w+n+1; T=S+1;
    for(int i=w+n;i>w;--i) link(S,i,1,0);
    for(int i=w;i;--i) link(i,T,1,0);
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j){
            scanf("%d",&q);
            for(int k=1;k<=n;++k)
                link(i+w,(j-1)*n+k,1,q*k);
        }
    while(bfs());
    printf("%.2f",tot*1.0/n);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/kafuuchino/p/10770486.html
今日推荐