您的位置:首页 > 理论基础 > 计算机网络

[网络流24题][CODEVS1237]餐巾计划问题(费用流)

2016-03-09 16:23 591 查看

题目描述

传送门

题解

拆点,把每天的点拆成xi和yi,xi表示每一天的脏毛巾,yi表示每一天的新毛巾。

从超级源向xi连边,容量为ri,费用为0;

从yi向超级汇连边,容量为ri,费用为0;

从超级源向yi连边,容量为INF,费用为p;

从xi分别向y(i+m)连边(i+m<=N),容量为INF,费用为f;

从xi分别向y(i+n)连边(i+n<=N),容量为INF,费用为m;

从xi向x(i+1)连边,容量为INF,费用为0;

以上建图分别表示:每天产生的脏毛巾;每天需要准备的新毛巾;每天新买进的毛巾;快洗毛巾;慢洗毛巾;延期送洗。

用最小费用最大流求解即可。

这道题的处理方法及其巧妙,注重理解。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;

const int max_N=1005;
const int max_Max=max_N*2+2;
const int max_m=max_Max*20;
const int max_e=max_m*2;
const int inf=1e9;

int N,p,m,f,n,s,Max,maxflow,mincost,r[max_N];
int point[max_Max],next[max_e],v[max_e],remain[max_e],c[max_e],tot;
int last[max_Max],dis[max_Max];
bool vis[max_Max];
queue <int> q;

inline void addedge(int x,int y,int cap,int z){
++tot; next[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=cap; c[tot]=z;
++tot; next[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0; c[tot]=-z;
}

inline int addflow(int s,int t){
int ans=inf,now=t;
while (now!=s){
ans=min(ans,remain[last[now]]);
now=v[last[now]^1];
}

now=t;
while (now!=s){
remain[last[now]]-=ans;
remain[last[now]^1]+=ans;
now=v[last[now]^1];
}

return ans;
}

inline bool bfs(int s,int t){
memset(dis,0x7f,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[s]=0;
vis[s]=true;
while (!q.empty()) q.pop();
q.push(s);

while (!q.empty()){
int now=q.front(); q.pop();
vis[now]=false;
for (int i=point[now];i!=-1;i=next[i])
if (dis[v[i]]>dis[now]+c[i]&&remain[i]){
dis[v[i]]=dis[now]+c[i];
last[v[i]]=i;
if (!vis[v[i]]){
vis[v[i]]=true;
q.push(v[i]);
}
}
}

if (dis[t]>inf) return false;
int flow=addflow(s,t);
maxflow+=flow;
mincost+=flow*dis[t];
return true;
}

inline void major(int s,int t){
maxflow=0; mincost=0;
while (bfs(s,t));
}

int main(){
tot=-1;
memset(point,-1,sizeof(point));
memset(next,-1,sizeof(next));

scanf("%d%d%d%d%d%d",&N,&p,&m,&f,&n,&s);
for (int i=1;i<=N;++i)
scanf("%d",&r[i]);
Max=N*2+2;

//从超级源向每天的脏毛巾连边,费用0,容量ri
for (int i=1;i<=N;++i)
addedge(1,1+i,r[i],0);

//从每天的新毛巾向超级汇连边,费用0,容量ri
for (int i=1;i<=N;++i)
addedge(1+N+i,Max,r[i],0);

//从超级源向每天的新毛巾连边,费用p,容量INF
for (int i=1;i<=N;++i)
addedge(1,1+N+i,inf,p);

//从每天的脏毛巾向下一天的脏毛巾连边(延期送洗),费用0,容量INF
for (int i=1;i<N;++i)
addedge(1+i,1+i+1,inf,0);

//从每天的脏毛巾向快洗或慢洗完成后的一天的新毛巾连边,费用为快洗或慢洗的费用,容量INF
for (int i=1;i<=N;++i){
if (i+m<=N)
addedge(1+i,1+N+i+m,inf,f);
if (i+n<=N)
addedge(1+i,1+N+i+n,inf,s);
}

major(1,Max);
printf("%d\n",mincost);
}


总结

①最小费用最大流是在保证最大流的基础上实现最小费用。那么我们就可以把最大流看成一个控制条件。意思就是满足题意的解必须建立在跑满最大流的基础之上。

②没有严格限制的边容量都可以设为INF,刚开始看起来貌似不科学,实际上有了该控制的条件的控制,这些边都可以看成是一种保证流量的关系,费用的话视情况而定。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: