[BZOJ2879][NOI2012]美食节(费用流)

时间:2021-11-22 20:41:07

设sm为所有p之和,套路地对每道菜建一个点,将每个厨师拆成sm个点,做的倒数第i道菜的代价为time*i。

S向每道菜连边<0,p[i]>(前者为代价后者为流量),i菜到j厨师的第k个点连<v[i][j]*k,1>,厨师到T连<0,1>。

但图太大了,于是动态加点。当厨师的第i个点被流完后再建第i+1个点,由于费用随i递增所以不会产生错误。

 #include<cstdio>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
#define For(i,x) for (int i=h[x],k; i; i=nxt[i])
using namespace std; const int N=,M=,inf=1e9;
int n,m,ans,S,T,cnt=,sm,mn,v[][],p[],h[N],d[N],pre[N],inq[N];
int to[M],nxt[M],val[M],fl[M],q[M]; void add(int u,int v,int c,int f){
to[++cnt]=v; val[cnt]=c; fl[cnt]=f; nxt[cnt]=h[u]; h[u]=cnt;
to[++cnt]=u; val[cnt]=-c; fl[cnt]=; nxt[cnt]=h[v]; h[v]=cnt;
} bool spfa(){
rep(i,,T) pre[i]=-,inq[i]=,d[i]=inf;
d[S]=; inq[S]=; q[]=S;
for (int st=,ed=; st!=ed; ){
int x=q[++st]; inq[x]=;
For(i,x) if (fl[i] && d[k=to[i]]>d[x]+val[i]){
d[k]=d[x]+val[i]; pre[k]=i;
if (!inq[k]) inq[k]=,q[++ed]=k;
}
}
return d[T]!=inf;
} void mcmf(){
for (ans=; spfa(); ans+=d[T]*mn){
mn=inf;
for (int i=pre[T]; ~i; i=pre[to[i^]]) mn=min(mn,fl[i]);
for (int i=pre[T]; ~i; i=pre[to[i^]]){
fl[i]-=mn; fl[i^]+=mn;
if (to[i]==T && (to[i^]-n)%sm){
int k=to[i^]+,x=(to[i^]-n)/sm+,y=(to[i^]-n)%sm+; add(k,T,,);
rep(j,,n) add(j,k,v[j][x]*y,);
}
}
}
} int main(){
freopen("bzoj2879.in","r",stdin);
freopen("bzoj2879.out","w",stdout);
scanf("%d%d",&n,&m);
rep(i,,n) scanf("%d",&p[i]),sm+=p[i];
rep(i,,n) rep(j,,m) scanf("%d",&v[i][j]);
S=n+m*sm+; T=S+;
rep(i,,n) add(S,i,,p[i]);
rep(i,,m) add(n+(i-)*sm+,T,,);
rep(i,,n) rep(j,,m) add(i,n+(j-)*sm+,v[i][j],);
mcmf(); printf("%d\n",ans);
return ;
}