题解 [SCOI2007]修车
题面
解析
这题要拆点。。
首先,证明一个式子:
设修理员M修了N辆车,
且修每辆车的时间为W1,W2....WN。
那么,这个修理员一共花的时间就为:W1*N+W2*(N-1)+...+WN*1。
因此,若i号修理员修第j辆车的时间为c[i][j],
将c[i][j]拆成1...n个点,
将其中第k个点与j号顾客(j号车)相连,
费用为c[i][j]*k,
最后将修理员与源点相连,顾客与汇点相连(反过来也可以),
流量为1,费用为0。
跑费用流就行了。
上AC代码:
//#include<bits/stdc++.h> #include <iostream> #include <cstdio> #include <cstring> #include <queue> using namespace std; inline int read(){ int sum=0,f=1;char ch=getchar(); while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0' && ch<='9'){sum=sum*10+ch-'0';ch=getchar();} return f*sum; } const int INF=0x3f3f3f3f; struct node{ int to,next,v,w; }e[100001]; struct hh{ int fa,edge; }pre[100001]; int n,m,s,t; int c[101][101]/*第i辆车给第j个人修的时间*/; int head[100001],cnt=1; int d[100001],inq[100001],mi[100001]; void add(int x,int y,int v,int w){ //printf("%d -> %d : %d\n",x,y,w); e[++cnt].to=head[x]; e[cnt].next=y; e[cnt].v=v;e[cnt].w=w; head[x]=cnt; e[++cnt].to=head[y]; e[cnt].next=x; e[cnt].v=0;e[cnt].w=-w; head[y]=cnt; } bool spfa(){ memset(d,0x3f,sizeof(d)); memset(inq,0,sizeof(inq)); memset(mi,0x3f,sizeof(mi)); queue <int> que; que.push(s); d[s]=0; while(!que.empty()){ int x=que.front(); que.pop(); inq[x]=0; for(int i=head[x];i;i=e[i].to){ int k=e[i].next; if(!e[i].v||d[k]<=d[x]+e[i].w) continue; d[k]=d[x]+e[i].w; pre[k].fa=x;pre[k].edge=i; mi[k]=min(mi[x],e[i].v); if(!inq[k]) que.push(k); inq[k]=1; } } return d[t]!=INF; } void EK(){ int ans=0; while(spfa()){ for(int i=t;i!=s;i=pre[i].fa){ e[pre[i].edge].v-=mi[t]; e[pre[i].edge^1].v+=mi[t]; } ans+=d[t]*mi[t]; } printf("%.2lf\n",(double)((double)ans/(double)n)); } int main(){ // freopen("fixed.in","r",stdin); // freopen("fixed.out","w",stdout); m=read();n=read(); s=1+(m+2)*n;t=2+(m+2)*n; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ c[i][j]=read(); } } for(int i=1;i<=m;i++){ for(int j=1;j<=n;j++){ add(s,i+m*j,1,0); for(int k=1;k<=n;k++){ add(i+m*j,k+m*n+n,1,c[k][i]*j); } } } for(int i=1;i<=n;i++){ add(i+m*n+n,t,1,0); } EK(); return 0; }