餐巾计划问题 线性规划与网络流24题之10 费用流

相关知识:最小费用(最大)流

问题描述:
一个餐厅在相继的N 天里, 每天需用的餐巾数不尽相同。 假设第i天需要ri块餐巾(i=1,
2,…,N)。餐厅可以购买新的餐巾,每块餐巾的费用为p分;或者把旧餐巾送到快洗部,
洗一块需 m天,其费用为 f 分;或者送到慢洗部,洗一块需 n 天(n>m),其费用为 s<f 分。
每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,以及多
少块保存起来延期送洗。 但是每天洗好的餐巾和购买的新餐巾数之和, 要满足当天的需求量。
试设计一个算法为餐厅合理地安排好N 天中餐巾使用计划,使总的花费最小。

 

问题分析:

       题目要求每天的餐巾够用,因此算法中的最大流已经确定,一天之中餐巾的来源可以是新买来的,快洗或是慢洗到这一天刚好洗完的;而每天用完的餐巾则是送去快洗或慢洗,或留到下一天统一处理,

       然后就是要把每天需要用的和用完的分开处理,这样就有了一个二分图模型。Xi表示第i天需要用的餐巾数量, 二分图X集合中顶点Xi表示第i天用完的餐巾,其数量为ri,所以从S向Xi连接容量为ri的边作为限制。Y集合中每个点Yi则是第i天需要的餐巾,数量为ri,与T连接的边容量作为限制。每天用完的餐巾可以选择留到下一天(Xi->Xi+1),不需要花费,送到快洗部(Xi->Yi+m),费用为f,送到慢洗部(Xi->Yi+n),费用为s。每天需要的餐巾除了刚刚洗好的餐巾,还可能是新购买的(S->Yi),费用为p。

具体连边方法:

把每天分为二分图两个集合中的顶点Xi,Yi,建立附加源S汇T。

1、从S向每个Xi连一条容量为ri,费用为0的有向边。//表示每天最多用ri餐巾
2、从每个Yi向T连一条容量为ri,费用为0的有向边。//同上
3、从S向每个Yi连一条容量为无穷大,费用为p的有向边。//Yi天新买的餐巾
4、从每个Xi向Xi+1(i+1<=N)连一条容量为无穷大,费用为0的有向边。//留到下一天处理
5、从每个Xi向Yi+m(i+m<=N)连一条容量为无穷大,费用为f的有向边。//慢洗,到i+m天才能用
6、从每个Xi向Yi+n(i+n<=N)连一条容量为无穷大,费用为s的有向边。//快洗,到i+n天才能用

      这样一个费用流就出现了。只要y点集合与T点连接的边全部满流,而且保证总费用最小,这题就AC了。

View Code
#include<cstdio>
#include<queue>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
#define MaxN 210
#define INF 2000000
struct atp
{
int y,x,flow,cost;
int op,next;
}e[MaxN];
queue<int> q;
int n,Newcost,kt,kc,mt,mc,s,t,tot,ans;
int first[MaxN],pre[MaxN],pe[MaxN],d[MaxN];
bool b[MaxN];
void add(int x,int y,int flow,int cost)
{
tot++;
e[tot].y=y;
e[tot].x=x;
e[tot].flow=flow;
e[tot].cost=cost;
e[tot].op=tot+1;
e[tot].next=first[x];
first[x]=tot;
tot++;
e[tot].y=x;
e[tot].x=y;
e[tot].flow=0;
e[tot].cost=cost;
e[tot].op=tot-1;
e[tot].next=first[y];
first[y]=tot;
}

void init()
{
int x;
scanf("%d%d%d%d%d%d",&n,&Newcost,&kt,&kc,&mt,&mc);
s=0;t=n+n+n;
for (int i=1;i<=n;i++)
{
scanf("%d",&x);
add(s,i,x,0);
add(n+i,t,x,0);
add(s,n+i,INF,Newcost);
if (i<n) add(i,i+1,INF,0);
if (i+kt<=n) add(i,i+n+kt,INF,kc);
if (i+mt<=n) add(i,i+n+mt,INF,mc);
}
}

bool spfa()
{

memset(b,false,sizeof(b));
memset(d,100,sizeof(d));
int INFF=d[1];
while (!q.empty()) q.pop();
b[s]=true;
d[s]=0;
q.push(s);
while (!q.empty())
{
int u=q.front();
for (int p=first[u];p;p=e[p].next)
{
if (d[u]+e[p].cost<d[e[p].y] && e[p].flow>0)
{
d[e[p].y]=d[u]+e[p].cost;
pre[e[p].y]=p;
if (!b[e[p].y])
{
b[e[p].y]=true;
q.push(e[p].y);
}
}
}
b[u]=false;
q.pop();
}
if (d[t]==INFF) return false; else return true;
}
void work()
{
int Minflow=INF,i,tt;
i=t;
while (i!=s)
{
tt=pre[i];
if (e[tt].flow<Minflow) Minflow=e[tt].flow;
i=e[tt].x;
}
i=t;
for (int i=t;i!=s;i=e[tt].x);
while (i!=s)
{
tt=pre[i];
e[tt].flow-=Minflow;
e[e[tt].op].flow+=Minflow;
ans+=Minflow*e[tt].cost;
i=e[tt].x;
}
}
int main()
{
init();
while (spfa()) work();
printf("%d",ans);
// system("pause");
return 0;
}


 

你可能感兴趣的:(网络流)