[NOI2012]美食節,P2050,最小費用最大流
阿新 • • 發佈:2018-12-04
正題
這一題是[SCOI2007]修車的加強版。(先看修車再做這題哦!)
首先是每種菜可能有多個人需求。
怎麼解決?我們把菜放左邊,人放右邊,然後左邊把流量設成就可以了。
由於我們每次增廣的流量是1,所以時間複雜度是很高的,是,k為SPFA的不定常數。
爆炸,而且建邊的空間和時間我們也承受不起。
我們再來考慮動態加邊。
一個廚師先選倒數第1道菜,再選倒數第2道菜。因為倒數第1道菜只用被算1次,倒數第二道菜會被算2次。
我們可以先把每一種菜連向每個廚師的倒數第一道菜。
假如a廚師的倒數第1道菜被選了,那麼就把a廚師的倒數第二道菜開放。
以此類推,一直到所有菜都被選完了。
// luogu-judger-enable-o2 #include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<queue> using namespace std; int n,m; int num[810]; int p[50][110]; struct edge{ int x,y,next,c,cos; }s[9000010]; int first[90010],d[90010],mmin[90010],last[90010],len=1; bool tf[90010]; int begin,end; int tot=0; queue<int> f; void ins(int x,int y,int c,int cos){ len++;s[len]=(edge){x,y,first[x],c,cos};first[x]=len; len++;s[len]=(edge){y,x,first[y],0,-cos};first[y]=len; } bool SPFA(int&flow,int&cost){ memset(tf,false,sizeof(tf));tf[begin]=true; memset(d,63,sizeof(d));d[begin]=0; mmin[begin]=1e9; f.push(begin); while(!f.empty()){ int x=f.front();f.pop();tf[x]=false; for(int i=first[x];i!=0;i=s[i].next){ int y=s[i].y; if(d[y]>d[x]+s[i].cos && s[i].c>0){ d[y]=d[x]+s[i].cos;mmin[y]=min(mmin[x],s[i].c);last[y]=i; if(!tf[y]){ tf[y]=true; f.push(y); } } } } if(d[end]==d[end+1]) return false; flow+=mmin[end]; cost+=mmin[end]*d[end]; int now=end; while(now!=begin){ s[last[now]].c-=mmin[end];s[last[now]^1].c+=mmin[end]; now=s[last[now]].x; } now=s[last[end]].x; int x=(now-n)/tot+1,y=(now-n)%tot+1; if((now-n)%tot!=0) for(int k=1;k<=n;k++) ins(k,now+1,1,p[k][x]*y); return true; } void MCMF(){ int flow=0,cost=0; while(SPFA(flow,cost)); printf("%d",cost); } int main(){ scanf("%d %d",&n,&m); int x; for(int i=1;i<=n;i++){ scanf("%d",&x); tot+=x; ins(begin,i,x,0); } end=n+tot*m+1; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&p[i][j]); for(int j=1;j<=m;j++) for(int k=1;k<=n;k++) ins(k,n+(j-1)*tot+1,1,p[k][j]*1); for(int i=n+1;i<end;i++) ins(i,end,1,0); MCMF(); }