BZOJ2879 NOI2012 美食节 费用流
题意:有N种菜,M位厨师,每位厨师做每种菜的时间不同。有K个人,每个人点ki个菜,求最小等待时间。
题解:
和BZOJ1070一个题,不过多了一个N,因此考虑优化。
显然决策倒数第K个菜的前提是倒数第K-1个菜已经决定完成,因此SPFA每搜出一条路径,就在这个厨师这里加边。说白了就是动点。
#include <queue> #include <cstdio> #include <cstring> #include <cstdlib> #include <climits> #include <iostream> #include <algorithm> using namespace std; const int MAXN=600+2; const int MAXV=200000+2; const int MAXM=300000+2; struct HASH{ int u; HASH *next; HASH(){} HASH(int _u,HASH *_next):u(_u),next(_next){} }*table[MAXV],mem[MAXM]; struct EDGE{ int u,v,c,w; EDGE(){} EDGE(int _u,int _v,int _c,int _w):u(_u),v(_v),c(_c),w(_w){} }e[MAXM]; int N,M,T,cnt=2,c[MAXN],w[MAXN][MAXN],cur[MAXV],d[MAXV],ans,tot; bool flag[MAXV]; queue<int> q; void Insert(int u,int v,int c,int w){ table[u]=&(mem[cnt]=HASH(cnt,table[u])),e[cnt++]=EDGE(u,v,c,w); table[v]=&(mem[cnt]=HASH(cnt,table[v])),e[cnt++]=EDGE(v,u,0,-w); } bool SPFA(int s,int t){ for(int i=s;i<=t;i++) d[i]=INT_MAX; d[s]=0,q.push(s),flag[s]=1; int x; while(!q.empty()){ x=q.front(),q.pop(); for(HASH *p=table[x];p;p=p->next) if(e[p->u].c && d[e[p->u].v]>d[x]+e[p->u].w){ d[e[p->u].v]=d[x]+e[p->u].w,cur[e[p->u].v]=p->u; if(!flag[e[p->u].v]) flag[e[p->u].v]=1,q.push(e[p->u].v); } flag[x]=0; } return d[t]<INT_MAX; } void Find(int s,int t){ int c=INT_MAX,a,x,y; for(int i=cur[t];i;i=cur[e[i].u]){ c=min(c,e[i].c); if(!e[i].u) a=e[i].v,x=(a-1)/tot+1,y=a%tot+1; } for(int i=cur[t];i;i=cur[e[i].u]){ e[i].c-=c,e[i^1].c+=c; ans+=e[i].w*c; } for(int i=1;i<=M;i++) Insert((x-1)*tot+y,N*tot+i,1,y*w[i][x]); } int main(){ scanf("%d%d",&M,&N); for(int i=1;i<=M;i++){ scanf("%d",c+i); tot+=c[i]; } for(int i=1;i<=M;i++) for(int j=1;j<=N;j++) scanf("%d",&w[i][j]); T=N*tot+M+1; for(int i=1;i<=N*tot;i++) Insert(0,i,1,0); for(int i=1;i<=M;i++) Insert(N*tot+i,T,c[i],0); for(int i=1;i<=N;i++) for(int k=1;k<=M;k++) Insert((i-1)*tot+1,N*tot+k,1,w[k][i]); while(SPFA(0,T)) Find(0,T); printf("%d",ans); return 0; }