【网络流】【BZOJ1070】【SCOI2007】修车
原题链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1070
题意:问你如何分配老司机使得每部车的等待时间之和最短。
解题思路:本题不易正做,考虑逆向解答,显然我们有对于一部车,我们枚举它被第i个老司机倒数第j个修,这样的带来的等待时间便是一定的。因此我们考虑枚举建点,建第j个老司机倒数第k个修第i部车,这样的话我们可以比较轻松的就可以建出一个网络,然后跑一遍最小费用最大流即可。
AC代码(用的比较low的SPFA费用流,但是在优化常数后就比学长写的P-D慢了一点)
#include<stdio.h> #include<string.h> #define inf 0x7fffffff #define min(a,b) (a<b?a:b) #define S 0 #define T 1001 #define length 2005 struct zxy{int to,next,c,v;}edge[200100]; int n,m,cnt=1,head[1005],dist[1005],que[2005],pre[1005]; bool vis[1005]; inline int in(){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9') f=ch=='-'?-1:1,ch=getchar(); while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x*f; } inline void ins(int x,int y,int v,int l){ edge[++cnt].to=y,edge[cnt].next=head[x],edge[cnt].v=v,edge[cnt].c=l,head[x]=cnt; edge[++cnt].to=x,edge[cnt].next=head[y],edge[cnt].v=0,edge[cnt].c=l*(-1),head[y]=cnt; } bool SPFA_costflow(int s,int e){ memset(dist,127/3,sizeof(dist)); register int h=0,t=1,w,v; que[1]=s; vis[s]=1; dist[s]=0; while(h!=t){ (++h)%=length; w=que[h]; for (register int i=head[w]; i; i=edge[i].next) if (dist[edge[i].to]>dist[w]+edge[i].c&&edge[i].v){ v=edge[i].to; pre[v]=i; dist[v]=dist[w]+edge[i].c; if (!vis[v]){ vis[v]=1; if (dist[v]<dist[h+1]){ que[h]=v; h=(h-1+length)%length; } else{ (++t)%=length; que[t]=v; } } } vis[w]=0; } return dist[e]!=dist[T+1]; } int cost_flow(int s,int t){ int cost=0; while(SPFA_costflow(s,t)){ int mi=inf; for (register int i=t; i; i=edge[pre[i]^1].to) mi=min(mi,edge[pre[i]].v); for (register int i=t; i; i=edge[pre[i]^1].to) edge[pre[i]].v-=mi,edge[pre[i]^1].v+=mi; cost+=dist[t]*mi; } return cost; } void init(){ m=in(),n=in(); for (int i=1; i<=n; ++i){ for (int j=1; j<=m; ++j){ int l=in(); for (int k=1; k<=n; ++k) ins(i,j*n+k,1,k*l); ins(j*n+i,T,1,0); } ins(S,i,1,0); } } int main(){ init(); printf("%.2lf",1.0*cost_flow(S,T)/n); }