经典网络流建模问题(餐巾)
餐巾
【问题描述】
一个餐厅在相继的N天里,第i天需要Ri块餐巾(i=l,2,…,N)。餐厅可以从三种途径获得餐巾。
(1)购买新的餐巾,每块需p分;
(2)把用过的餐巾送到快洗部,洗一块需m天,费用需f分(f<p)。如m=l时,第一天送到快洗部的餐巾第二天就可以使用了,送慢洗的情况也如此。
(3)把餐巾送到慢洗部,洗一块需n天(n>m),费用需s分(s<f)。
在每天结束时,餐厅必须决定多少块用过的餐巾送到快洗部,多少块送慢洗部。在每天开始时,餐厅必须决定是否购买新餐巾及多少,使洗好的和新购的餐巾之和满足当天的需求量Ri,并使N天总的费用最小。
【输入】
输入文件共3行,第1行为总天数;第2行为每天所需的餐巾块数;第3行为每块餐巾的新购费用p,快洗所需天数m,快洗所需费用f,慢洗所需天数n,慢洗所需费用s。
【输出】
输出1行为最小的费用。
【样例】
输入:
3
3 2 4
10 1 6 2 3
输出:
64
很好的一个题目,让我又加深了对最小费用最大流的问题。一开始自己“意淫”了一个最小费用流的图,结果样例一直算的是180,后来才发现自己还是没有理解好最小费用最大流的含义,所谓的“最小费用最大流”,首要保证的条件是最大流,如果最优的情况未必是原图达到“最大流”时的情况的话,那么是不能用“最小费用最大流”算法去解决的。
那么既然上面yy出了问题,不妨就先着手解决这个问题:要将“最大流”赋予怎样的含义(也就是说确定是什么东西达到“最大”)才能保证最优情况是原图达到“最大流”时的情况呢?一种可行的思路就是确定每天使用的餐巾的数量达到“最大”,不管是用的旧的还是新买的,反正总要用那么多,这样最优情况一定是满足“最大流”条件的。那么我们就要继续考虑这些使用的东西是从哪里来的,一部分是新买的,另一部分是用剩下的洗了之后的,新买的好办,直接建个源点流出来费用为P的流即可,那么洗了之后的那部分是怎么来的?肯定首先要满足是剩下的,那么怎么才会剩下呢?每天用了多少就会剩下多少。想到这,其实大部分问题已经解决了,剩下的就是考虑脏的餐巾存放及清洗的问题了,这部分放到建图里一起说吧。
接下来我们考虑建图,首先将一个点i拆成两个点i和i',一共要建六类边:
① (S,i,n,P) :S到i的容量为n(n表示当天需要的餐巾的数量)费用为P的边,表示今天最多买n条新的餐巾,当然把容量搞成INF也无所谓。
② (i,T,n,0) :i到T的容量为n费用为0的边,表示今天最多用n条餐巾,通过这样的边保证了每天的最大流都是所需餐巾的数量。
③ (S,i',n,0) :S到i'的容量为n费用为0的边,表示今天会剩下n条餐巾,之所以费用为0是因为这些是剩下的,之前肯定已经买过了,所以费用是0。这里的容量就不能搞成INF了,一个原因是因为最后一定至多只会生剩下n条,另一个原因就是这里如果搞成INF会造成错误,因为会凭空多了好多用过的餐巾,而这些餐巾是没有被买过的。
④ (i',i+xn,INF,xw) :i'到i+xn(xn表示快洗部洗一条餐巾所需的天数)的容量为INF费用为xw(xw表示快洗部洗一条餐巾所需的费用)的边,表示累计到今天的剩下的部分或者全部的餐巾可以放到快洗部去洗,并由第i+xn天使用。
⑤ (i',i+yn,INF,yw) :和上面那类边基本一样,只不过现在是慢洗部了。
⑥ (i',(i+1)',INF,0) : i'到(i+1)'的容量为INF费用为0的边,表示第i天用脏了的餐巾可以积攒到第i+1天再做打算。
建完上面的边之后做最小费用最大流就可以了。
#include<cstdio> #include<cstring> #include<deque> #define N ((2500<<1)+10)*2 #define M 100000+10 #define inf 1e9 using namespace std; namespace MincostMaxflow{ int head[N],arnum=1; struct Arc{int next,to,cap,cost;}arc[M]; void add(int from,int to,int cap,int cost){ arc[++arnum].next=head[from]; head[from]=arnum; arc[arnum].to=to; arc[arnum].cap=cap; arc[arnum].cost=cost; } void insert(int from,int to,int cap,int cost){add(from,to,cap,cost);add(to,from,0,-cost);} int dis[N],pre[N],way[N],book[N]; int st,en; deque<int>Q; int Mincost,Maxflow; bool SPFA() { for(int i=0;i<=N-1;i++)dis[i]=inf; memset(pre,0,sizeof(pre)); memset(way,0,sizeof(way)); memset(book,0,sizeof(book)); Q.clear(); book[st]=1;dis[st]=0; Q.push_back(st); while(!Q.empty()) { int u=Q.front();book[u]=0; Q.pop_front(); for(int i=head[u];i;i=arc[i].next) { int v=arc[i].to; int cap=arc[i].cap; if(cap>0&&dis[v]>dis[u]+arc[i].cost) { dis[v]=dis[u]+arc[i].cost; way[v]=u; pre[v]=i; if(!book[v]) { book[v]=1; if(Q.empty()||dis[v]>dis[Q.front()])Q.push_back(v); else Q.push_front(v); } } } } if(dis[en]==inf)return false; int minn=inf; for(int i=en;i!=st;i=way[i]) minn=min(minn,arc[pre[i]].cap); Maxflow+=minn; Mincost+=minn*dis[en]; for(int i=en;i!=st;i=way[i]) { arc[pre[i]].cap-=minn; arc[pre[i]^1].cap+=minn; } return true; } } using namespace MincostMaxflow; int main() { int D; scanf("%d",&D); st=0,en=N-1; int day[N]; for(int i=1;i<=D;i++)scanf("%d",&day[i]); int p,m,f,n,s;scanf("%d%d%d%d%d",&p,&m,&f,&n,&s); for(int i=1;i<=D;i++){ insert(st,i,day[i],p); insert(i,en,day[i],0); insert(st,i+N/2,day[i],0); insert(i+N/2,i+m,inf,f); insert(i+N/2,i+n,inf,s); insert(i+N/2,i+N/2+1,inf,0); } while(SPFA()); printf("%d\n",Mincost); return 0; }