[NOI2012]美食節(費用流)
題目描述
CZ市為了歡迎全國各地的同學,特地舉辦了一場盛大的美食節。作為一個喜歡嚐鮮的美食客,小M自然不願意錯過這場盛宴。他很快就嚐遍了美食節所有的美食。然而,嚐鮮的慾望是難以滿足的。儘管所有的菜品都很可口,廚師做菜的速度也很快,小M仍然覺得自己桌上沒有已經擺在別人餐桌上的美食是一件無法忍受的事情。於是小M開始研究起了做菜順序的問題,即安排一個做菜的順序使得同學們的等待時間最短。小M發現,美食節共有n種不同的菜品。每次點餐,每個同學可以選擇其中的一個菜品。總共有m個廚師來製作這些菜品。當所有的同學點餐結束後,菜品的製作任務就會分配給每個廚師。然後每個廚師就會同時開始做菜。廚師們會按照要求的順序進行製作,並且每次只能製作一人份。此外,小M還發現了另一件有意思的事情: 雖然這m個廚師都會製作全部的n種菜品,但對於同一菜品,不同廚師的製作時間未必相同。他將菜品用1, 2, ..., n依次編號,廚師用1, 2, ..., m依次編號,將第j個廚師製作第i種菜品的時間記為 ti,j 。小M認為:每個同學的等待時間為所有廚師開始做菜起,到自己那份菜品完成為止的時間總長度。換句話說,如果一個同學點的菜是某個廚師做的第k道菜,則他的等待時間就是這個廚師製作前k道菜的時間之和。而總等待時間為所有同學的等待時間之和。現在,小M找到了所有同學的點菜資訊: 有 pi 個同學點了第i種菜品(i=1, 2, ..., n)。他想知道的是最小的總等待時間是多少。
題解
一開始按照修車那題的思路搞了個費用流,就是把廚師拆成tim個點,表示他是倒數第幾個做的,然後邊權就是對後面的人的代價和,然後T到飛起。
原因是直接把圖建出來導致圖的規模過大,然後spfa就死了。
所以我們可以動態加邊,每增廣一次就加一個點。
程式碼
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #define N 49 #define M 109 #define P 809 #define R register using namespacestd; queue<int>q; int p[N],head[M*P],tot=1,dis[M*P],pre[M*P],fl[M*P],ans,n,m,sum,a[N][M],tim[M*P],id[M],be[M*P]; bool vis[M*P]; inline int rd(){ int x=0;char c=getchar();bool f=0; while(!isdigit(c)){if(c=='-')f=1;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}return f?-x:x; } struct edge{int n,to,l,f;}e[N*M*P*2]; inline void add(int u,int v,int l,int f){ e[++tot].n=head[u];e[tot].to=v;head[u]=tot;e[tot].l=l;e[tot].f=f; e[++tot].n=head[v];e[tot].to=u;head[v]=tot;e[tot].l=0;e[tot].f=-f; } inline bool spfa(int s,int t){ memset(dis,0x3f,sizeof(dis)); q.push(s);dis[s]=0;fl[s]=2e9; while(!q.empty()){ int u=q.front();q.pop();vis[u]=0; // cout<<u<<" "<<dis[u]<<endl; for(R int i=head[u];i;i=e[i].n){ int v=e[i].to; if(e[i].l&&dis[v]>dis[u]+e[i].f){ dis[v]=dis[u]+e[i].f;pre[v]=i;fl[v]=min(fl[u],e[i].l); if(!vis[v]){vis[v]=1;q.push(v);} } } } return dis[t]!=0x3f3f3f3f; } inline void calc(int s,int t){ int x=t; while(x!=s){ int i=pre[x]; e[i].l-=fl[t];e[i^1].l+=fl[t];x=e[i^1].to; } ans+=dis[t]*fl[t]; } int main(){ n=rd();m=rd(); for(R int i=1;i<=n;++i)p[i]=rd(),sum+=p[i]; for(R int i=1;i<=n;++i)for(R int j=1;j<=m;++j)a[i][j]=rd(); int now=n; for(R int i=1;i<=m;++i)for(R int j=1;j<=sum;++j){ ++now;tim[now]=j==1?1:tim[now-1]+1;if(j==1)id[i]=now;be[now]=i; } for(R int i=1;i<=n;++i)for(R int j=1;j<=m;++j)add(i,id[j],1,a[i][j]); for(R int i=1;i<=m;++i)add(id[i],now+1,1,0); for(R int i=1;i<=n;++i)add(0,i,p[i],0); while(spfa(0,now+1)){ calc(0,now+1); int x=e[pre[now+1]^1].to;x++; add(x,now+1,1,0); for(int i=1;i<=n;++i)add(i,x,1,tim[x]*a[i][be[x]]); } cout<<ans; return 0; }