P1251餐厅计划问题(最小费用最大流)
题目描述
一个餐厅在相继的 NN 天里,每天需用的餐巾数不尽相同。假设第 ii 天需要 r_iri块餐巾( i=1,2,...,N)。餐厅可以购买新的餐巾,每块餐巾的费用为 pp 分;或者把旧餐巾送到快洗部,洗一块需 m 天,其费用为 f 分;或者送到慢洗部,洗一块需 nn 天(n>mn>m),其费用为 ss 分(s<fs<f)。
每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,以及多少块保存起来延期送洗。但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。
试设计一个算法为餐厅合理地安排好 NN 天中餐巾使用计划,使总的花费最小。编程找出一个最佳餐巾使用计划。
输入格式
由标准输入提供输入数据。文件第 1 行有 1 个正整数 NN,代表要安排餐巾使用计划的天数。
接下来的一行是餐厅在相继的 NN 天里,每天需用的餐巾数。
最后一行包含5个正整数p,m,f,n,sp,m,f,n,s。pp 是每块新餐巾的费用; mm 是快洗部洗一块餐巾需用天数; ff 是快洗部洗一块餐巾需要的费用; nn 是慢洗部洗一块餐巾需用天数; ss 是慢洗部洗一块餐巾需要的费用。
输出格式
将餐厅在相继的 N 天里使用餐巾的最小总花费输出
题解:
/* * P1251餐巾计划问题 * 题意: * 一个餐厅在相继的n天里,每天需用的餐巾数不同。 * 假设第i天需要ri块餐巾。 * 餐厅可以购买新的餐巾,每块餐巾的费用为p分。 * 或者把旧餐巾送到快洗部,洗一块需要m天,其费用为f分。 * 或者送到慢洗部,洗一块需要n天,其费用为s分。 * 每次结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块脏的餐巾送到慢洗部,以及多少块保存起来延期送洗, * 但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。 * 输出在N天里使用餐巾的最小总花费。 * 题解: * 最小费用最大流。 * 首先,拆点,把每天拆成早上和晚上。每天晚上会受到脏餐巾,每天早上又有干净的餐巾。 * 1.从源点向每天晚上连一条流量为当天所用餐巾x,费用为0的边,表示每天晚上从起点获得x条脏餐巾。 * 2.从每一天早上向汇点连一条流量为当天所用餐巾x,费用为0的边,每天白天,表示从汇点提供x条干净的餐巾,流满时表示第i天的餐巾够用。 * 3.每一天晚上向第二天晚上连一条流量为inf,费用为0的边,表示每天晚上可以将脏餐巾留到第二天晚上。 * 4.每一天晚上向这一天+快洗所用天数那一天早上连一条流量为inf,费用为快洗所用钱数的边,表示每天晚上可以送去快洗部,在第i+t1天早上收到餐巾。 * 5.同理,每一天晚上向这一天+慢洗所用天数的那一天早上连一条流量为inf,费用为慢洗所用钱数的边,表示每天晚上可以送去慢洗部,在第i+t2天早上收到餐巾。 * 6.从起点向每天早上连一条流量为inf,费用为购买餐巾所用钱数的边,表示每天早上可以购买餐巾。 */ #include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e5+100; const ll inf=1e18; int head[maxn]; int tot; int s,t; ll dis[maxn]; int pre[maxn]; int lst[maxn]; ll flow[maxn]; int visit[maxn]; ll maxflow; ll mincost; struct node { int u,v; ll dis,flow; int nxt; }edge[maxn]; void addedge (int u,int v,ll flow,ll dis) { edge[tot].u=u; edge[tot].v=v; edge[tot].flow=flow; edge[tot].dis=dis; edge[tot].nxt=head[u]; head[u]=tot++; } bool spfa (int s,int t) { for (int i=0;i<maxn;i++) dis[i]=inf,flow[i]=inf,visit[i]=0; queue<int> q; q.push(s); visit[s]=1; dis[s]=0; pre[t]=-1; while (!q.empty()) { int u=q.front(); q.pop(); visit[u]=0; for (int i=head[u];i!=-1;i=edge[i].nxt) { if (edge[i].flow>0&&dis[edge[i].v]>dis[u]+edge[i].dis) { dis[edge[i].v]=dis[u]+edge[i].dis; pre[edge[i].v]=u; lst[edge[i].v]=i; flow[edge[i].v]=min(flow[u],edge[i].flow); if (!visit[edge[i].v]) { visit[edge[i].v]=1; q.push(edge[i].v); } } } } return pre[t]!=-1; } void MCMF () { while (spfa(s,t)) { int u=t; maxflow+=flow[t]; mincost+=flow[t]*dis[t]; while (u!=s) { edge[lst[u]].flow-=flow[t]; edge[lst[u]^1].flow+=flow[t]; u=pre[u]; } } } int a[maxn]; int main () { memset(head,-1,sizeof(head)); int n; int p,m,f,_n,_s; scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); scanf("%d%d%d%d%d",&p,&m,&f,&_n,&_s); //1-2*n代表每天的早上和晚上 s=0,t=2*n+1; for (int i=1;i<=n;i++) { addedge(s,i+n,a[i],0); addedge(i+n,s,0,0); } for (int i=1;i<=n;i++) { addedge(i,t,a[i],0); addedge(t,i,0,0); } for (int i=1;i<n;i++) { addedge(i+n,i+n+1,inf,0); addedge(i+n+1,i+n,0,0); } for (int i=1;i<=n;i++) { if (i+m>n) continue; addedge(i+n,i+m,inf,f); addedge(i+m,i+n,0,-f); } for (int i=1;i<=n;i++) { if (i+_n>n) continue; addedge(i+n,i+_n,inf,_s); addedge(i+_n,i+n,0,-_s); } for (int i=1;i<=n;i++) { addedge(s,i,inf,p); addedge(i,s,0,-p); } MCMF(); printf("%lld\n",mincost); }