BZOJ 1070 修车(费用流)
如果能想到费用流,这道题就是显然了。
要求所有人的等待平均时间最小,也就是所有人的总等待时间最小。
每辆车只需要修一次,所以s连每辆车容量为1,费用为0的边。 现在需要把每个人拆成n个点,把车和每个人的第k个点连一条容量为1,费用为cost[i][j]*k的边。
最后把每个人拆完后的点向汇点连一条容量为1,费用为0的边。
#include<iostream> #include<cstdio> #include<cstring> #define inf 0x7fffffff #define T 601 using namespace std; int n,m,cnt=1,ans,t[61][10]; int d[605],q[605],from[605],head[605]; bool mark[605]; struct edge{int from,to,next,c,v;}e[100001]; void ins(int u,int v,int w,int c) { cnt++; e[cnt].from=u;e[cnt].to=v; e[cnt].next=head[u];head[u]=cnt; e[cnt].c=c;e[cnt].v=w; } void insert(int u,int v,int w,int c) {ins(u,v,w,c);ins(v,u,0,-c);} bool spfa() { memset(mark,0,sizeof(mark)); for(int i=0;i<=T;i++)d[i]=inf; int t=0,w=1; d[T]=0;mark[T]=1;q[0]=T; while(t!=w) { int now=q[t];t++;if(t==T)t=0; for(int i=head[now];i;i=e[i].next) if(e[i^1].v&&d[e[i].to]>d[now]-e[i].c) { d[e[i].to]=d[now]-e[i].c; if(!mark[e[i].to]) {mark[e[i].to]=1;q[w++]=e[i].to;if(w==T)w=0;} } mark[now]=0; } if(d[0]==inf)return 0; return 1; } int dfs(int x,int f) { if(x==T){mark[T]=1;return f;} int used=0,w; mark[x]=1; for(int i=head[x];i;i=e[i].next) if(!mark[e[i].to]&&e[i].v&&d[x]-e[i].c==d[e[i].to]) { w=f-used; w=dfs(e[i].to,min(e[i].v,w)); ans+=w*e[i].c; e[i].v-=w;e[i^1].v+=w; used+=w;if(used==f)return f; } return used; } void zkw() { while(spfa()) { mark[T]=1; while(mark[T]) { memset(mark,0,sizeof(mark)); dfs(0,inf); } } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) scanf("%d",&t[i][j]); for(int i=1;i<=n*m;i++) insert(0,i,1,0); for(int i=n*m+1;i<=n*m+m;i++) insert(i,T,1,0); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int k=1;k<=m;k++) insert((i-1)*m+j,n*m+k,1,t[k][i]*j); zkw(); printf("%.2lf",(double)ans/m); return 0; }