问题描述:
一个餐厅在相继的N 天里,每天需用的餐巾数不尽相同。假设第i天需要ri块餐巾(i=1,
2,…,N)。餐厅可以购买新的餐巾,每块餐巾的费用为p分;或者把旧餐巾送到快洗部,
洗一块需m天,其费用为f 分;或者送到慢洗部,洗一块需n 天(n>m),其费用为s<f 分。
每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,以及多
少块保存起来延期送洗。但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。
试设计一个算法为餐厅合理地安排好N 天中餐巾使用计划,使总的花费最小。
编程任务:
编程找出一个最佳餐巾使用计划.
数据输入:
由文件input.txt提供输入数据。文件第1 行有6 个正整数N,p,m,f,n,s。N 是要安排餐巾
使用计划的天数;p 是每块新餐巾的费用;m 是快洗部洗一块餐巾需用天数;f 是快洗部洗
一块餐巾需要的费用;n是慢洗部洗一块餐巾需用天数;s是慢洗部洗一块餐巾需要的费用。
接下来的N 行是餐厅在相继的N 天里,每天需用的餐巾数。
结果输出:
程序运行结束时,将餐厅在相继的N 天里使用餐巾的最小总花费输出到文件output.txt
中。
输入示例 输出示例
3 10 2 3 3 2 145
5
6
7
一个餐厅在相继的N 天里,每天需用的餐巾数不尽相同。假设第i天需要ri块餐巾(i=1,
2,…,N)。餐厅可以购买新的餐巾,每块餐巾的费用为p分;或者把旧餐巾送到快洗部,
洗一块需m天,其费用为f 分;或者送到慢洗部,洗一块需n 天(n>m),其费用为s<f 分。
每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,以及多
少块保存起来延期送洗。但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。
试设计一个算法为餐厅合理地安排好N 天中餐巾使用计划,使总的花费最小。
编程任务:
编程找出一个最佳餐巾使用计划.
数据输入:
由文件input.txt提供输入数据。文件第1 行有6 个正整数N,p,m,f,n,s。N 是要安排餐巾
使用计划的天数;p 是每块新餐巾的费用;m 是快洗部洗一块餐巾需用天数;f 是快洗部洗
一块餐巾需要的费用;n是慢洗部洗一块餐巾需用天数;s是慢洗部洗一块餐巾需要的费用。
接下来的N 行是餐厅在相继的N 天里,每天需用的餐巾数。
结果输出:
程序运行结束时,将餐厅在相继的N 天里使用餐巾的最小总花费输出到文件output.txt
中。
输入示例 输出示例
3 10 2 3 3 2 145
5
6
7
分析:
很明显的一个网络优化问题,最小费用最大流。约束条件是每天要有足够的干净的餐巾,可能是快洗的,慢洗的,或者是全新的。每天的餐巾分成两个顶点,一个是全新的要用的X,一个是用完的Y。建立源点汇点,S,T。S连接所有的Y点,容量为每天的需求量,费用0。所有Y点连接T,容量也为每天的需求,费用为0。然后为Y的来源连接边。所有X点向Y+m连接,容量无穷,费用为f,所有X点向Y+n点连接,容量为无穷,费用为s。这里要说的是容量是当天所用的量或者无穷都没关系,因为S与该点的容量限制了数目,所以这里的连接不影响结果,只要足够大就行。然后把所有的Y和Y+1点连接,表明干净的多余的可以留着明天用。然后把S和第一个Y点连接费用为p,容量为无穷。这样就OK了。
值得一提的我这种建模方法是不连接X和X+1点的,换句话说要么连接X和X+1,要么连接Y和Y+1。连接X和X+1表明脏的餐巾可以留到第二天,然后再考虑洗不洗。而Y和Y+1表明多余的干净的餐巾可以留到第二天再考虑分配,这样费用为P的边需要一条就足够了,第一天买足量的新餐巾可以往后滞留。而如果是X和X+1的连法,则需要对每个Y点都需要与S连接一条容量无穷费用为p的边。
我的代码用的是我的解法,另一种解法就是连边不同而已,但是最后的答案是一样的,可以自行百度。如果看不懂分析的同学可以用纸和笔自己画一画就明白了。
最后要说的是这不是二分图,因为Y之间或者X之间是相连的。
代码:
#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 808*2;
const int INF = 1<<30;
int S,T;
int d[maxn],p[maxn],a[maxn];
bool inqueue[maxn];
struct edge{
int from,to,cap,flow,cost;
edge(int a,int b,int c,int d,int f):from(a),to(b),cap(c),flow(d),cost(f){}
};
vector<edge> edges;
vector<int> g[maxn];
void addedge(int from,int to,int cap,int cost)
{
edges.push_back(edge(from,to,cap,0,cost));
edges.push_back(edge(to,from,0,0,-cost));
int m=edges.size();
g[from].push_back(m-2);
g[to].push_back(m-1);
}
bool bellman_spfa(int &flow,int &cost)
{
for(int i=0;i<=T;i++) d[i]=INF;
memset(inqueue,false,sizeof(inqueue));
d[S]=0;inqueue[S]=true; p[S]=0;a[S]=INF;
queue<int >q;
q.push(S);
while(!q.empty()){
int x=q.front();q.pop();inqueue[x]=false;
for(int i=0;i<g[x].size();i++){
edge &e =edges[g[x][i]];
if(e.cap>e.flow&&d[e.to]>d[x]+e.cost){
d[e.to]=d[x]+e.cost;
p[e.to]=g[x][i];
a[e.to]=min(a[x],e.cap-e.flow);
if(!inqueue[e.to]){ inqueue[e.to]=true;q.push(e.to); }
}
}
}
if(d[T]==INF) return false;
flow+=a[T];
cost+=d[T]*a[T];
int x=T;
while(x!=S){
edges[p[x]].flow+=a[T];
edges[p[x]^1].flow-=a[T];
x=edges[p[x]].from;
}
return true;
}
int mincost()
{
int flow=0,cost=0;
while(bellman_spfa(flow,cost));
return cost;
}
int main()
{
int N,p,m,f,n,s,need;
scanf("%d%d%d%d%d%d",&N,&p,&m,&f,&n,&s);
S=2*N;T=S+1;
for(int i=0;i<N;i++){
scanf("%d",&need);
if(i==0) addedge(S,i,INF,p);//买新餐巾的边
if(i+m<N) addedge(i+N,i+m,INF,f);//旧的送快洗
if(i+n<N) addedge(i+N,i+n,INF,s);//旧的送慢洗
if(i+1<N) addedge(i,i+1,INF,0);//新的可以留着第二天用
addedge(i,T,need,0);//容量限制为每天的需求
addedge(S,i+N,need,0);//同上
}
printf("%d\n",mincost());
return 0;
}