zoukankan      html  css  js  c++  java
  • 经典网络流建模问题(餐巾)

       餐巾

    【问题描述】
      一个餐厅在相继的N天里,第i天需要Ri块餐巾(i=l,2,…,N)。餐厅可以从三种途径获得餐巾。
        (1)购买新的餐巾,每块需p分;
        (2)把用过的餐巾送到快洗部,洗一块需m天,费用需f分(f<p)。如m=l时,第一天送到快洗部的餐巾第二天就可以使用了,送慢洗的情况也如此。
        (3)把餐巾送到慢洗部,洗一块需n天(n>m),费用需s分(s<f)。
        在每天结束时,餐厅必须决定多少块用过的餐巾送到快洗部,多少块送慢洗部。在每天开始时,餐厅必须决定是否购买新餐巾及多少,使洗好的和新购的餐巾之和满足当天的需求量Ri,并使N天总的费用最小。
    【输入】
        输入文件共3行,第1行为总天数;第2行为每天所需的餐巾块数;第3行为每块餐巾的新购费用p,快洗所需天数m,快洗所需费用f,慢洗所需天数n,慢洗所需费用s。
    【输出】
        输出1行为最小的费用。
    【样例】
    输入:
    3
    3 2 4
    10 1 6 2 3
    输出:
    64

      很好的一个题目,让我又加深了对最小费用最大流的问题。一开始自己“意淫”了一个最小费用流的图,结果样例一直算的是180,后来才发现自己还是没有理解好最小费用最大流的含义,所谓的“最小费用最大流”,首要保证的条件是最大流如果最优的情况未必是原图达到“最大流”时的情况的话,那么是不能用“最小费用最大流”算法去解决的。

        那么既然上面yy出了问题,不妨就先着手解决这个问题:要将“最大流”赋予怎样的含义(也就是说确定是什么东西达到“最大”)才能保证最优情况是原图达到“最大流”时的情况呢?一种可行的思路就是确定每天使用的餐巾的数量达到“最大”,不管是用的旧的还是新买的,反正总要用那么多,这样最优情况一定是满足“最大流”条件的。那么我们就要继续考虑这些使用的东西是从哪里来的,一部分是新买的,另一部分是用剩下的洗了之后的,新买的好办,直接建个源点流出来费用为P的流即可,那么洗了之后的那部分是怎么来的?肯定首先要满足是剩下的,那么怎么才会剩下呢?每天用了多少就会剩下多少。想到这,其实大部分问题已经解决了,剩下的就是考虑脏的餐巾存放及清洗的问题了,这部分放到建图里一起说吧。

        接下来我们考虑建图,首先将一个点i拆成两个点i和i',一共要建六类边:

        ① (S,i,n,P) :S到i的容量为n(n表示当天需要的餐巾的数量)费用为P的边,表示今天最多买n条新的餐巾,当然把容量搞成INF也无所谓。

        ② (i,T,n,0) :i到T的容量为n费用为0的边,表示今天最多用n条餐巾,通过这样的边保证了每天的最大流都是所需餐巾的数量。

        ③ (S,i',n,0) :S到i'的容量为n费用为0的边,表示今天会剩下n条餐巾,之所以费用为0是因为这些是剩下的,之前肯定已经买过了,所以费用是0。这里的容量就不能搞成INF了,一个原因是因为最后一定至多只会生剩下n条,另一个原因就是这里如果搞成INF会造成错误,因为会凭空多了好多用过的餐巾,而这些餐巾是没有被买过的。

        ④ (i',i+xn,INF,xw) :i'到i+xn(xn表示快洗部洗一条餐巾所需的天数)的容量为INF费用为xw(xw表示快洗部洗一条餐巾所需的费用)的边,表示累计到今天的剩下的部分或者全部的餐巾可以放到快洗部去洗,并由第i+xn天使用。

        ⑤ (i',i+yn,INF,yw) :和上面那类边基本一样,只不过现在是慢洗部了。

        ⑥ (i',(i+1)',INF,0) : i'到(i+1)'的容量为INF费用为0的边,表示第i天用脏了的餐巾可以积攒到第i+1天再做打算。

        建完上面的边之后做最小费用最大流就可以了。

    #include<cstdio>
    #include<cstring>
    #include<deque>
    #define N ((2500<<1)+10)*2
    #define M 100000+10
    #define inf 1e9
    using namespace std;
    namespace MincostMaxflow{
        int head[N],arnum=1;
        struct Arc{int next,to,cap,cost;}arc[M];
        void add(int from,int to,int cap,int cost){
            arc[++arnum].next=head[from];
            head[from]=arnum;
            arc[arnum].to=to;
            arc[arnum].cap=cap;
            arc[arnum].cost=cost;
        }
        void insert(int from,int to,int cap,int cost){add(from,to,cap,cost);add(to,from,0,-cost);}
        int dis[N],pre[N],way[N],book[N];
        int st,en;
        deque<int>Q;
        int Mincost,Maxflow;
        bool SPFA()
        {
            for(int i=0;i<=N-1;i++)dis[i]=inf;
            memset(pre,0,sizeof(pre));
            memset(way,0,sizeof(way));
            memset(book,0,sizeof(book));
            Q.clear();
            book[st]=1;dis[st]=0;
            Q.push_back(st);
            while(!Q.empty())
            {
                int u=Q.front();book[u]=0;
                Q.pop_front();
                for(int i=head[u];i;i=arc[i].next)
                {
                    int v=arc[i].to;
                    int cap=arc[i].cap;
                    if(cap>0&&dis[v]>dis[u]+arc[i].cost)
                    {
                        dis[v]=dis[u]+arc[i].cost;
                        way[v]=u;
                        pre[v]=i;
                        if(!book[v])
                        {
                            book[v]=1;
                            if(Q.empty()||dis[v]>dis[Q.front()])Q.push_back(v);
                            else Q.push_front(v);
                            
                        }
                    }
                }
            }
            if(dis[en]==inf)return false;
            int minn=inf;
            for(int i=en;i!=st;i=way[i])
                minn=min(minn,arc[pre[i]].cap);
            Maxflow+=minn;
            Mincost+=minn*dis[en];
            for(int i=en;i!=st;i=way[i])
            {
                arc[pre[i]].cap-=minn;
                arc[pre[i]^1].cap+=minn;
            }
            return true;
        }
    }
    using namespace MincostMaxflow;
    int main()
    {
        int D;
        scanf("%d",&D); 
        st=0,en=N-1;
        int day[N];
        for(int i=1;i<=D;i++)scanf("%d",&day[i]);
        int p,m,f,n,s;scanf("%d%d%d%d%d",&p,&m,&f,&n,&s);
        for(int i=1;i<=D;i++){
            insert(st,i,day[i],p);
            insert(i,en,day[i],0);
            insert(st,i+N/2,day[i],0);
            insert(i+N/2,i+m,inf,f);
            insert(i+N/2,i+n,inf,s);
            insert(i+N/2,i+N/2+1,inf,0);
        }
        while(SPFA());
        printf("%d
    ",Mincost);
        return 0;
    }
  • 相关阅读:
    阮一峰的网络日志
    解决Windows路径太长的目录以及文件名超长删除的问题
    LVM扩容根分区
    Centos7 搭建prometheus+Grafana监控
    python3 读取txt文件数据,绘制趋势图,matplotlib模块
    linux python3安装whl包时报错解决:is not a supported wheel on this platform
    堆排序
    nginx: [emerg] "upstream" directive is not allowed here in .../.../.../*.conf
    Github私有仓库使用设置
    AWK处理重复Hosts
  • 原文地址:https://www.cnblogs.com/star-eternal/p/7635081.html
Copyright © 2011-2022 走看看