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

问题描述:
一个餐厅在相继的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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值