1. 程式人生 > >[BZOJ2879][NOI2012]美食節(費用流)

[BZOJ2879][NOI2012]美食節(費用流)

for 動態加點 rep algorithm col spa sin pen namespace

設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遞增所以不會產生錯誤。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 4
#define For(i,x) for (int i=h[x],k; i; i=nxt[i]) 5 using namespace std; 6 7 const int N=1000010,M=7000010,inf=1e9; 8 int n,m,ans,S,T,cnt=1,sm,mn,v[50][120],p[50],h[N],d[N],pre[N],inq[N]; 9 int to[M],nxt[M],val[M],fl[M],q[M]; 10 11 void add(int u,int v,int c,int f){ 12 to[++cnt]=v; val[cnt]=c; fl[cnt]=f; nxt[cnt]=h[u]; h[u]=cnt;
13 to[++cnt]=u; val[cnt]=-c; fl[cnt]=0; nxt[cnt]=h[v]; h[v]=cnt; 14 } 15 16 bool spfa(){ 17 rep(i,1,T) pre[i]=-1,inq[i]=0,d[i]=inf; 18 d[S]=0; inq[S]=1; q[1]=S; 19 for (int st=0,ed=1; st!=ed; ){ 20 int x=q[++st]; inq[x]=0; 21 For(i,x) if (fl[i] && d[k=to[i]]>d[x]+val[i]){
22 d[k]=d[x]+val[i]; pre[k]=i; 23 if (!inq[k]) inq[k]=1,q[++ed]=k; 24 } 25 } 26 return d[T]!=inf; 27 } 28 29 void mcmf(){ 30 for (ans=0; spfa(); ans+=d[T]*mn){ 31 mn=inf; 32 for (int i=pre[T]; ~i; i=pre[to[i^1]]) mn=min(mn,fl[i]); 33 for (int i=pre[T]; ~i; i=pre[to[i^1]]){ 34 fl[i]-=mn; fl[i^1]+=mn; 35 if (to[i]==T && (to[i^1]-n)%sm){ 36 int k=to[i^1]+1,x=(to[i^1]-n)/sm+1,y=(to[i^1]-n)%sm+1; add(k,T,0,1); 37 rep(j,1,n) add(j,k,v[j][x]*y,1); 38 } 39 } 40 } 41 } 42 43 int main(){ 44 freopen("bzoj2879.in","r",stdin); 45 freopen("bzoj2879.out","w",stdout); 46 scanf("%d%d",&n,&m); 47 rep(i,1,n) scanf("%d",&p[i]),sm+=p[i]; 48 rep(i,1,n) rep(j,1,m) scanf("%d",&v[i][j]); 49 S=n+m*sm+1; T=S+1; 50 rep(i,1,n) add(S,i,0,p[i]); 51 rep(i,1,m) add(n+(i-1)*sm+1,T,0,1); 52 rep(i,1,n) rep(j,1,m) add(i,n+(j-1)*sm+1,v[i][j],1); 53 mcmf(); printf("%d\n",ans); 54 return 0; 55 }

[BZOJ2879][NOI2012]美食節(費用流)