【網路流24題】餐巾計劃問題
阿新 • • 發佈:2021-07-15
標籤:費用流
Solution
由於每天餐廳需要 \(r_i\) 份餐巾,產生 \(r_i\) 份需清洗餐巾,考慮將每天拆成兩個點 \(X_i\) 與 \(Y_i\) ,其中 \(X_i\) 需要向匯點 \(T\) 匯入 \(r_i\) 份餐巾,\(Y_i\) 向 \(X_j\) 匯入清洗過的餐巾。
從源點 \(S\) 向 \(X_i\) 連一條容量無限,單位費用為 \(p\) 的邊,表示購買餐巾;向 \(Y_i\) 連一條容量為 \(r_i\),單位費用為 \(0\) 的邊,表示該天用過的餐巾。
此外,\(Y_i\) 向 \(Y_{i+1}\) 連一條容量無限,單位費用為 \(0\) 的邊,表示延期清洗餐巾;分別向 \(X_{i+m}\)
最後跑一邊最小費用最大流即可。
Code
#include<bits/stdc++.h> #define M 4005 using namespace std; typedef long long ll; typedef double db; char IO; int rd(){ int num=0;bool f=0; while(IO=getchar(),IO<48||IO>57)if(IO=='-')f=1; do num=(num<<1)+(num<<3)+(IO^48); while(IO=getchar(),IO>=48&&IO<=57); return f?-num:num; } bool f2; int n,m,S,T; int to[M*12],nxt[M*12],hd[M],cnte=1; ll val[M*12],cost[M*12]; void Adde(int u,int v,ll w,ll c){ to[++cnte]=v;val[cnte]=w;cost[cnte]=c; nxt[cnte]=hd[u];hd[u]=cnte; } void Add(int u,int v,ll w,ll c){ Adde(u,v,w,c);Adde(v,u,0,-c); } ll dep[M]; int cur[M]; bool vis[M]; queue<int> Q; bool SPFA(){ memset(dep,63,sizeof(dep)); memcpy(cur,hd,sizeof(cur)); dep[S]=1;Q.push(S); int u,v; while(!Q.empty()){ u=Q.front();Q.pop(); vis[u]=0; for(int i=hd[u];i;i=nxt[i]){ v=to[i]; if(val[i]&&dep[u]+cost[i]<dep[v]){ dep[v]=dep[u]+cost[i]; if(!vis[v])Q.push(v),vis[v]=1; } } } return dep[T]!=0x3f3f3f3f3f3f3f3f; } ll micost; ll DFS(int u,ll flow){ if(u==T)return flow; vis[u]=1; ll res=0,f; for(int i=cur[u],v;i;i=nxt[i]){ cur[u]=i;v=to[i]; if(vis[v]||dep[v]!=dep[u]+cost[i])continue; if(val[i]&&(f=DFS(v,min(flow,val[i])))>0){ val[i]-=f;val[i^1]+=f; res+=f;flow-=f; micost+=f*cost[i]; } } vis[u]=0; return res; } ll C[M]; bool f1; int main(){ // cout<<1.0*(&f1-&f2)/1024.0/1024.0<<endl; n=rd();S=0,T=2*n+1; for(int i=1;i<=n;++i) C[i]=rd(); ll p=rd(),a=rd(),f=rd(),b=rd(),s=rd(); for(int i=1;i<=n;++i){ Add(S,i,1e18,p); Add(i,T,C[i],0); Add(S,n+i,C[i],0); if(i+a<=n)Add(n+i,i+a,1e18,f); if(i+b<=n)Add(n+i,i+b,1e18,s); if(i!=n)Add(n+i,n+i+1,1e18,0); } while(SPFA())DFS(S,1e18); cout<<micost; return 0; }