餐巾计划问题 zwk费用流解法

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

建立源点S汇点T,并把每一天拆分为入点与出点,

以ai表示第i天需要的餐巾数量

S向每天的入点连一条容量ai,费用0的边表示新产生的脏餐巾数

每天的入点向下一天的入点连一条容量无限大,费用0的边表示转移到下一天的脏餐巾

每天的入点向m天后的出点连一条容量无限大,费用f的边表示送去快洗部洗餐巾

每天的入点向n天后的出点连一条容量无限大,费用s的边表示送去慢洗部洗餐巾

每天的出点向T连一条容量ai,费用0的边表示当天需要的干净餐巾

zwk费用流的实现细节解释在代码注释中

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
using namespace std;
const int MAXN=5010000;
const int maxn=5010;
const int INF=~0U>>1;
long long int maxflow=0,cost=0;
int v[maxn],dis[maxn];
int r[maxn];
int S,T;
int N,p,m,f,n,s;
int tot=0;
int pointer[maxn];
struct Edge
{
    int to,next,cap,op,f,w;
    Edge() {};
    Edge(int b,int c,int nxt,int num,int flow,int weight) {to=b,cap=c,next=nxt,op=num^1,f=flow,w=weight;}
}edge[MAXN];
inline void addedge(int a,int b,int c,int w1)
{
    edge[tot]=Edge(b,c,pointer[a],tot,0,w1);
    pointer[a]=tot++;
    edge[tot]=Edge(a,0,pointer[b],tot,0,-w1);
    pointer[b]=tot++;
}
inline int aug(int x,int flow)      //dfs搜增光路 flow代表当前dfs路径中剩余容量最小边的容量
{
    if(x==T)
    {
        maxflow+=flow;
        cost+=dis[S]*flow;
        return flow;
    }
    v[x]=1;
    int l=flow;
    for(int j=pointer[x];j!=-1;j=edge[j].next)
    {
        int y=edge[j].to;
        if(!v[y]&&edge[j].cap-edge[j].f&&dis[y]+edge[j].w==dis[x])  //距离标号满足要求代表走的这一步是目前情况下到达y的最短路其中的一步
        {
            int tmp=aug(y,min(l,edge[j].cap-edge[j].f));        //
            edge[j].f+=tmp,edge[j^1].f-=tmp,l-=tmp;         //x-->y这条路分走了tmp大小的流量
            if(!l) return flow;                             //到达x点的flow大小的流量被分完了就不必考虑x的其他子节点了
        }
    }
    return flow-l;
}
inline bool modlabel()
{
    int  minh=INF;
    rep(i,S,T)
    {
        if(v[i])
            for(int j=pointer[i];j!=-1;j=edge[j].next)
            {
                int y=edge[j].to;
                if(edge[j].cap-edge[j].f>0&&!v[y])
                {
                    minh=min(minh,dis[y]+edge[j].w-dis[i]);
                }
            }
    }
    if(minh==INF) return 0;
    rep(i,S,T) if(v[i]) dis[i]+=minh;           //利用距离标号控制最短路条件
    return 1;
}
inline void zwk()
{
    do
    {
        do
        {
            rep(i,S,T) v[i]=0;
        }while(aug(S,INF));
    }while(modlabel());
}
inline void init()
{
    memset(pointer,-1,sizeof(pointer));
    scanf("%d%d%d%d%d%d",&N,&p,&m,&f,&n,&s);
    S=0;T=2*N+1;
    int ri;
    rep(i,1,N)
    {
        scanf("%d",&ri);
        addedge(S,i,ri,0);
        addedge(N+i,T,ri,0);
        addedge(S,N+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<N) addedge(i,i+1,INF,0);
    }
}
int main()
{
    freopen("napk10.in","r",stdin);
    init();
    zwk();
    printf("%lld\n",cost);
    return 0;
}
时间: 2024-10-12 00:20:46

餐巾计划问题 zwk费用流解法的相关文章

【Codevs1237&amp;网络流24题餐巾计划】(费用流)

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

[luoguP1251] 餐巾计划问题(费用流)

传送门 模型 网络优化问题,用最小费用最大流解决. 实现 把每天分为二分图两个集合中的顶点Xi,Yi,建立附加源S汇T. 1.从S向每个Xi连一条容量为ri,费用为0的有向边. 2.从每个Yi向T连一条容量为ri,费用为0的有向边. 3.从S向每个Yi连一条容量为无穷大,费用为p的有向边. 4.从每个Xi向Xi+1(i+1<=N)连一条容量为无穷大,费用为0的有向边. 5.从每个Xi向Yi+m(i+m<=N)连一条容量为无穷大,费用为f的有向边. 6.从每个Xi向Yi+n(i+n<=N)

P1251 餐巾计划问题 (费用流)

题目链接 方法: 重点在建图!!!将一天拆成晚上和早上: 1. 从源点向每一天晚上连一条流量为当天所用餐巾x,费用为0的边: 2. 每一天早上向汇点连一条流量为当天所用餐巾x,费用为0的边: 3. 从每一天晚上向第二天晚上连一条流量为INF,费用为0的边,表示每天晚上可以将脏餐巾留到第二天晚上: 4. 从每一天晚上向这一天+快洗所用天数t1的那一天早上连一条流量为INF,费用为快洗所用钱数的边,表示每天晚上可以送去快洗部,在地i+t1天早上收到餐巾 : 5. 从每一天晚上向这一天+慢洗所用天数t

线性规划费用流解法(Bzoj1061: [Noi2008]志愿者招募)

题面 传送门 Sol 线性规划费用流解法用与求解未知数为非负数的问题 这道题可以列出一堆形如 \(x[i]+x[j]+x[k]+...>=a[p]\) 的不等式 我们强行给每个式子减去一个东西,使他变成这样 \(x[i]+x[j]+x[k]+...-y[p]==a[p]\) 然后相邻两个式子差分一下 把每个式子看成一个点 那么这样后,在这个题中所有的未知数只会出现在一个方程中 等式左边符号是正的向符号为负的方程连边,费用为代价,如果是补的未知数\(y\),那么费用为零 右边的数是正的连\(s\)

BZOJ 1221 HNOI 2001 软件开发/网络流24题 餐巾计划问题 最小费用最大流

题目大意:有一个软件公司,每天需要给一些员工准备消毒毛巾,这些毛巾可以循环利用,但是需要消毒.可以将毛巾送去消毒,有两种方式,A天fA花费,B天fB花费.或者还可以直接买新毛巾,问为了满足员工的需求,至少需要花多少钱. 思路:经典的费用流问题.将每一天拆点,S向每一天<<1连边,约束每一天需要多少毛巾:每一天<<1|1向T连边,约束每一天需要的毛巾.每一天<<1向这一天清洗的毛巾能够使用的那一天<<1|1,注意A和B.毛巾可以延后使用,那么每一天<&l

刷题总结——学姐的逛街计划(vijos1891费用流)

题目: doc 最近太忙了, 每天都有课. 这不怕, doc 可以请假不去上课.偏偏学校又有规定, 任意连续 n 天中, 不得请假超过 k 天. doc 很忧伤, 因为他还要陪学姐去逛街呢. 后来, doc发现, 如果自己哪一天智商更高一些, 陪学姐逛街会得到更多的好感度.现在 doc 决定做一个实验来验证自己的猜想, 他拜托 小岛 预测出了 自己 未来 3n 天中, 每一天的智商.doc 希望在之后的 3n 天中选出一些日子来陪学姐逛街, 要求在不违反校规的情况下, 陪学姐逛街的日子自己智商的

【网络流24题】餐巾计划问题(最小费用最大流)

[网络流24题]餐巾计划问题(最小费用最大流) 题面 COGS 洛谷上的数据范围更大,而且要开longlong 题解 餐巾的来源分为两种: ①新买的 ②旧的拿去洗 所以,两种情况分别建图 先考虑第一种 因为新买餐巾没有任何限制,并且随时可以买 所以直接从源点向每一天连边,容量为INF,费用为餐巾的价格 因为流要流出去,所以每个点向汇点连边,容量为每天的用量,费用为0 第二种,旧的拿去洗 首先考虑一下怎么算有多少旧的餐巾 每天用旧的餐巾的数量值一定的,不可能变多 因此从源点向这些点连边,容量为每天

餐巾计划问题(费用流)

拆点,建二分图,Xi表示第i天用完的餐巾,Yi表示第i天需要的餐巾,求费用流. //http://www.cnblogs.com/IMGavin/ #include <iostream> #include <stdio.h> #include <cstdlib> #include <cstring> #include <queue> #include <vector> #include <map> #include <

[网络流24题] 餐巾计划问题 [费用流]

题面: https://www.luogu.org/problemnew/show/P1251 思路: 这道题乍一看,可以跑上下界费用流 代码量.难度 -> inf 其实不然,我们可以用费用流的特殊处理去掉下界 观察题目,每天要求有ri块餐巾 首先,有贪心如下: 当且仅当每天可供使用的餐巾正好满足需求时,可以有最小费用 证明:若某一天有多一块餐巾,则其根本来源一定是买多了,而且在这块餐巾参与的周转中还消费了一些清洗费用,同时它造成其余的日子里也会有餐巾被闲置 因此首先把题目转化为"每天正好