P2050 [NOI2012]美食节
傳送門
看一眼,這不是修車原題嗎?
把廚師拆成 $n*m$ 個(gè)點(diǎn),第 $k$ 種菜連向第 $i$ 個(gè)廚師的第 $j$ 個(gè)點(diǎn)表示第 $i$ 個(gè)廚師倒數(shù)做的第 $j$ 個(gè)菜是 $k$
可以發(fā)現(xiàn)廚師 $i$ 做的倒數(shù)第 $j$ 個(gè)菜的貢獻(xiàn)是 $j*time[i][j]$ ($time[i][j]$ 表示廚師 $i$ 做第 $j$ 種菜需要的時(shí)間)
連邊 $(S,k,num[k],0)$ 表示第 $k$ 種菜要做 $num[k]$ 個(gè)
連邊 $(k,cooker[i][j],1,j*time[i][k])$ 表示第 $k$ 種菜給廚師 $i$ 留到倒數(shù)第 $j$ 個(gè)做,需要的時(shí)間就是 $j*tim[i][k]$
連邊 $(cooker[i][j],T,1,0)$ 表示廚師不能同時(shí)做好幾種菜
然后跑最小費(fèi)用最大流
然后 $T$ 成 $60$ 分...
考慮優(yōu)化,發(fā)現(xiàn)費(fèi)用流時(shí)的時(shí)間主要耗在玄學(xué)的 $SPFA$ 上了,發(fā)現(xiàn) $SPFA$ 的復(fù)雜度和邊數(shù)有很大關(guān)系
考慮刪掉沒用的邊
但是,好像每一條邊都有用...
考慮一下費(fèi)用流的過程,每次只找一條增廣路,并且顯然廚師一定不會(huì)閑著(不會(huì)出現(xiàn)第 $i$ 個(gè)位置沒有流,第 $i+1$ 個(gè)位置有流)
所以我們可以動(dòng)態(tài)地加邊,每次找完一條增廣路后再加可能會(huì)用到的邊
具體看代碼
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> using namespace std; typedef long long ll; inline int read() {int x=0,f=1; char ch=getchar();while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }return x*f; } const int N=1e5+7,M=1e7+7,INF=1e9+7; int fir[N],from[M],to[M],val[M],cst[M],cntt=1; inline void add(int a,int b,int c,int d) {from[++cntt]=fir[a]; fir[a]=cntt;to[cntt]=b; val[cntt]=c; cst[cntt]=d;from[++cntt]=fir[b]; fir[b]=cntt;to[cntt]=a; val[cntt]=0; cst[cntt]=-d; } int dis[N],mif[N],pre[N],S,T; queue <int> q; bool inq[N]; bool SPFA() {for(int i=S;i<=T;i++) dis[i]=INF;q.push(S); inq[S]=1; dis[S]=0,mif[S]=INF;while(!q.empty()){int x=q.front(); q.pop(); inq[x]=0;for(int i=fir[x];i;i=from[i]){int &v=to[i]; if(!val[i]||dis[v]<=dis[x]+cst[i]) continue;dis[v]=dis[x]+cst[i]; pre[v]=i;mif[v]=min(mif[x],val[i]);if(!inq[v]) q.push(v),inq[v]=1;}}return dis[T]<INF; } int n,m,p[2007][2007],tot;//p[i][j]表示第i種菜給第j個(gè)廚師需要的時(shí)間 ll ans; void upd() {for(int now=T,i=pre[T]; now!=S; now=to[i^1],i=pre[now])val[i]-=mif[T],val[i^1]+=mif[T];ans+=1ll*mif[T]*dis[T];int v=to[pre[T]^1],u=(v-n-1)/tot+1,t=(v-n)%tot;//u存廚師,t存此時(shí)是倒數(shù)第幾個(gè)做的菜add(v+1,T,1,0);//向廚師的下一個(gè)點(diǎn)連邊for(int i=1;i<=n;i++) add(i,v+1,1,p[i][u]*(t+1));//每個(gè)菜都可以讓此廚師做 } int main() {n=read(),m=read(); int a;for(int i=1;i<=n;i++) a=read(),add(S,i,a,0),tot+=a;S=0,T=n+m*tot+1;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++) { p[i][j]=read(); add(i,n+(j-1)*tot+1,1,p[i][j]); }//初始邊for(int i=1;i<=m;i++) add(n+(i-1)*tot+1,T,1,0);//初始邊while(SPFA()) upd();printf("%lld",ans);return 0; }?
轉(zhuǎn)載于:https://www.cnblogs.com/LLTYYC/p/10792765.html
與50位技術(shù)專家面對面20年技術(shù)見證,附贈(zèng)技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的P2050 [NOI2012]美食节的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C#上传文件处理
- 下一篇: 想要使用 for循环,就要添加 索引器