「网络流24题」餐巾计划问题

题目描述 Description

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

输入描述 Input Description

第 1 行有 6 个正整数 N,p,m,f,n,s。N 是要安排餐巾使用计划的天数;p 是每块新餐巾的费用;m 是快洗部洗一块餐巾需用天数;f 是快洗部洗一块餐巾需要的费用;n 是慢洗部洗一块餐巾需用天数;s 是慢洗部洗一块餐巾需要的费用。接下来的 N 行是餐厅在相继的 N 天里,每天需用的餐巾数。

输出描述 Output Description

将餐厅在相继的 N 天里使用餐巾的最小总花费输出

样例输入 Sample Input

3 10 2 3 3 2

5

6

7

样例输出 Sample Output

145 

题目分析

【问题分析】

网络优化问题,用最小费用最大流解决。

【建模方法】

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

  题目所说的每天需用ri块餐巾,我们可以这么理解,把一天拆成两个点Xi和Yi(上午和下午),一天既然需要用ri块餐巾,则必定会消耗ri块,所以我们把Xi表示为这一天接收到的干净的餐巾数,Yi表示为今天要清洗的餐巾数,那么就好建边了.

  从Xi-->X(i+1)建一条(inf,0)的边,表示此边容量无穷大,费用为0,理解为前一天可以把没用完的餐巾留到后一天

  从源点S-->每个Xi建一条(inf,p)的边,表示无论如何你至少可以通过买餐巾来达到需求

  从源点S-->每个Yi建一条(ri,0)的边

  从每个Xi-->汇点T点建一条(ri,0)的边

  从每个Yi-->X(i+m)建一条(inf,f)的边表示每天可以送餐巾到快洗部,费用为f

  同理从每个Yi-->X(i+n)建一条(inf,s)的边表示每天可以送餐巾到慢洗部,费用为s

求网络最小费用最大流,费用流值就是要求的最小总花费。

【建模分析】

  这个问题的主要约束条件是每天的餐巾够用,而餐巾的来源可能是最新购买,也可能是前几天送洗,今天刚刚洗好的餐巾。每天用完的餐巾可以选择送到快洗部或慢洗部,或者留到下一天再处理。

  在网络上求出的最小费用最大流,满足了问题的约束条件(因为在这个图上最大流一定可以使与T连接的边全部满流,其他边只要有可行流就满足条件),而且还可以保证总费用最小,就是我们的优化目标。

  然后直接跑费用流模板

  上代码

  

#include<iostream>
#include<cstdio>
#include<string>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<queue>
#define in(i) (i=read())
#define inf (2147483647)
using namespace std;
typedef long long lol;//就是要注意一点因为最大值赋的是int类型最大值,所以有乘法运算就会爆int,so开long long
lol read()
{
    lol ans=0,f=1;
    char i=getchar();
    while(i<'0'||i>'9')
    {
        if(i=='-') f=-1;
        i=getchar();
    }
    while(i>='0'&&i<='9')
    {
        ans=(ans<<3)+(ans<<1)+i-'0';
        i=getchar();
    }
    return ans*f;
}
struct edge
{
    lol to,next,cap,v;
}e[40010];
lol head[40010];
lol lev[4010],vis[4010];
lol len=1;
void add(lol a,lol b,lol c,lol d)
{
    e[++len].to=b;
    e[len].next=head[a];
    e[len].cap=c;
    e[len].v=d;
    head[a]=len;
}
lol pre[3010];
bool bfs(lol s,lol t)
{
    queue<lol>q;
    memset(lev,127/3,sizeof(lev));
    q.push(s);
    lev[s]=0;
    while(!q.empty())
    {
        lol x=q.front();
        q.pop();
        for(lol i=head[x];i;i=e[i].next)
        {
            lol to=e[i].to;
            if(e[i].cap>0&&lev[to]>lev[x]+e[i].v)
            {
                lev[to]=lev[x]+e[i].v;
                pre[to]=i;
                q.push(to);
            }
        }
    }
    if(lev[t]<lev[0]) return 1;
    return 0;
}
lol change(lol s,lol t)
{
    lol x=t,flow=inf,ans=0;
    while(x!=s)
    {
        flow=min(flow,e[pre[x]].cap);
        x=e[pre[x]^1].to;
    }
    x=t;
    while(x!=s)
    {
        e[pre[x]].cap-=flow;
        e[pre[x]^1].cap+=flow;
        ans+=flow*e[pre[x]].v;
        x=e[pre[x]^1].to;
    }
    return ans;
}
int main()
{
    lol tot=0;
    lol n,r;
    lol p,a,f,b,s;
    in(n);
    for(lol i=1;i<=n;i++)
    {
        in(r);
        add(i,(n<<1)+2,r,0);//Xi-->T
        add((n<<1)+2,i,0,0);
        add((n<<1)+1,i+n,r,0);//S-->Yi
        add(i+n,(n<<1)+1,0,0);
    }
    in(p);
    for(lol i=1;i<=n;i++)
    {
        add((n<<1)+1,i,inf,p);//S-->Xi,买餐巾
        add(i,(n<<1)+1,0,-p);
    }
    for(lol i=1;i<n;i++)
    {
        add(i,i+1,inf,0);//前一天留到第二天
        add(i+1,i,0,0);
    }
    in(a);in(f);
    for(lol i=1;i<=n-a;i++)
    {
        add(i+n,i+a,inf,f);//送到快洗部
        add(i+a,i+n,0,-f);
    }
    in(b);in(s);
    for(lol i=1;i<=n-b;i++)
    {
        add(i+n,i+b,inf,s);//送到慢洗部
        add(i+b,i+n,0,-s);
    }
    while(bfs((n<<1)+1,(n<<1)+2))
    {
        tot+=change((n<<1)+1,(n<<1)+2);
    }
    cout<<tot<<endl;
    return 0;
}
博主蒟蒻,随意转载.但必须附上原文链接
http://www.cnblogs.com/real-l/
原文地址:https://www.cnblogs.com/real-l/p/8412951.html