網路流24題 餐巾計劃
【題目描述】
一個餐廳在相繼的 n 天裡,每天需用的餐巾數不盡相同。假設第 i天需要 ri 塊餐巾。餐廳可以購買新的餐巾,每塊餐巾的費用為 P 分;或者把舊餐巾送到快洗部,洗一塊需 M 天,其費用為 F 分;或者送到慢洗部,洗一塊需 N 天,其費用為 S 分(S< F )。
每天結束時,餐廳必須決定將多少塊髒的餐巾送到快洗部,多少塊餐巾送到慢洗部,以及多少塊儲存起來延期送洗。但是每天洗好的餐巾和購買的新餐巾數之和,要滿足當天的需求量。
試設計一個演算法為餐廳合理地安排好 n 天中餐巾使用計劃,使總的花費最小。
【輸入格式】
第 1 行有 6 個正整數 n 、P、M、F、N、S。
n 是要安排餐巾使用計劃的天數, P 是每塊新餐巾的費用, M 是快洗部洗一塊餐巾需用天數,F 是快洗部洗一塊餐巾需要的費用,N 是慢洗部洗一塊餐巾需用天數,S 是慢洗部洗一塊餐巾需要的費用。
接下來的 n 行是餐廳在相繼的 n 天裡,每天需用的餐巾數。
【輸出格式】
輸出餐廳在相繼的 n天裡使用餐巾的最小總花費。
【樣例輸入】
3 10 2 3 3 2
5
6
7
【樣例輸出】
145
【備註】
1<=n<=1000
【題目分析】
網路流的關鍵依然在建圖上,此題建圖非常巧妙:
1.首先將每一天拆為兩個點,增加S,T兩個源匯點。
2.由S向每一天連一條容量為ri,費用為0的邊,最後最大流即各邊之和,每一天的拆的點向T連一條容量為ri,費用為0的邊。
3.由S向每一天所拆點連一條容量為INF,費用為P的邊,表示每天購買的餐巾數。
4.由第i天向第i+1天(i<n)連一條容量為INF,費用為0的邊,表示延遲送洗的餐巾。
5.由第i天向第i+M天(i+M<=n)所拆點連一條容量為INF,費用為F的邊,表示送快洗部洗的餐巾。
6.由第i天向第i+N天(i+N<=n)所拆點連一條容量為INF,費用為S的邊,表示送慢洗部洗的餐巾。
7.最後在圖上跑一遍最小費用最大流即可,答案即為最小費用。
【程式碼~】
#include<bits/stdc++.h> using namespace std; const int MAXN=2e3+10; const int MAXM=1e5+10; const int INF=0x3f3f3f3f; int n,m,s,t,cnt,cost; int head[MAXN],dis[MAXN],vis[MAXN],work[MAXN]; int nxt[MAXN],to[MAXN],w[MAXN],c[MAXN]; queue<int> q; void Add(int u,int v,int f,int p) { nxt[cnt]=head[u]; head[u]=cnt; to[cnt]=v; w[cnt]=f; c[cnt]=p; cnt++; } void add(int u,int v,int f,int p) { Add(u,v,f,p); Add(v,u,0,-p); } bool SPFA() { while(!q.empty()) q.pop(); memset(dis,INF,sizeof(dis)); memset(work,0,sizeof(work)); dis[s]=0; q.push(s); while(!q.empty()) { int u=q.front(); q.pop(); vis[u]=0; for(int i=head[u];i!=-1;i=nxt[i]) { int v=to[i]; if(dis[v]>dis[u]+c[i]&&w[i]) { dis[v]=dis[u]+c[i]; if(!vis[v]) { vis[v]=1; q.push(v); } } } } return dis[t]<INF; } int dfs(int u,int dist) { if(u==t) { cost+=dist*dis[t]; return dist; } work[u]=1; int res=0; for(int i=head[u];i!=-1;i=nxt[i]) { int v=to[i]; if(dis[v]==dis[u]+c[i]&&!work[v]&&w[i]) { int di=dfs(v,min(dist-res,w[i])); if(di) { w[i]-=di; w[i^1]+=di; res+=di; if(res==dist) break; } } } return res; } int dinic() { while(SPFA()) dfs(s,INF); return cost; } int main() { memset(head,-1,sizeof(head)); memset(nxt,-1,sizeof(nxt)); int P,M,F,N,S; scanf("%d%d%d%d%d%d",&n,&P,&M,&F,&N,&S); s=0,t=n+n+1; for(int i=1;i<=n;++i) { int ri; scanf("%d",&ri); add(s,i,ri,0); add(i+n,t,ri,0); add(s,i+n,INF,P); if(i+1<=n) add(i,i+1,INF,0); if(i+M<=n) add(i,i+M+n,INF,F); if(i+N<=n) add(i,i+N+n,INF,S); } printf("%d\n",dinic()); return 0; }