题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2879
题意:有n道菜,每道菜需要b[i]份,m个厨师,第j个厨师做第i道菜需要时间a[i][j],求做完所有菜,所有人等待的最小总时间。
思路:设所有的菜为sum。一个明显的思路是将每个厨师拆成sum个点。然后sum个菜每个菜向每个厨师的每个点连边,表示该道菜为该厨师第几个做。由于这样数据太大。动态加边。每次增光一次后找到此次增广的厨师,每道菜将其连边。
struct node { int u,v,next,cost,cap; }; node edges[N*5]; int head[N],e; void add(int u,int v,int cap,int cost) { e++; edges[e].u=u; edges[e].v=v; edges[e].cap=cap; edges[e].cost=cost; edges[e].next=head[u]; head[u]=e; } void Add(int u,int v,int cap,int cost) { add(u,v,cap,cost); add(v,u,0,-cost); } int pre[N],F[N],C[N],visit[N]; int SPFA(int s,int t,int n) { int i; for(i=0;i<=n;i++) F[i]=0,C[i]=INF,visit[i]=0; queue<int> Q; Q.push(s); F[s]=INF; C[s]=0; int u,v,cost,cap; while(!Q.empty()) { u=Q.front(); Q.pop(); visit[u]=0; for(i=head[u];i;i=edges[i].next) { if(edges[i].cap>0) { v=edges[i].v; cost=edges[i].cost; cap=edges[i].cap; if(C[v]>C[u]+cost) { C[v]=C[u]+cost; F[v]=min(F[u],cap); pre[v]=i; if(!visit[v]) visit[v]=1,Q.push(v); } } } } return F[t]; } int s,t,n,m,a[55][105],b[105],cnt[105],last[105]; int main() { RD(n,m); int sum=0,i,j,x; FOR1(i,n) RD(b[i]),sum+=b[i]; e=1; s=0; t=n+m+sum+1; FOR1(i,n) { Add(s,i,b[i],0); FOR1(j,m) { RD(a[i][j]); Add(i,n+j,1,a[i][j]); } } FOR1(i,m) { cnt[i]=1; Add(n+i,t,1,0); last[i]=e; } int ans=0,temp; while(sum--) { temp=SPFA(s,t,t); for(i=t;i!=s;i=edges[pre[i]].u) { x=pre[i]; ans+=temp*edges[x].cost; edges[x].cap-=temp; edges[x^1].cap+=temp; } for(j=1;j<=m&&edges[last[j]-1].cap;j++); cnt[j]++; FOR1(i,n) Add(i,n+m+sum,1,a[i][j]*cnt[j]); Add(n+m+sum,t,1,0); last[j]=e; } PR(ans); }