[Luogu P1251] [网络流24题] 餐巾计划问题

洛谷传送门

题目描述

一个餐厅在相继的 N N 天里,每天需用的餐巾数不尽相同。假设第 i 天需要 ri r i 块餐巾 (i=1,2,...,N) ( i = 1 , 2 , . . . , N ) 。餐厅可以购买新的餐巾,每块餐巾的费用为 p p 分;或者把旧餐巾送到快洗部,洗一块需 m 天,其费用为 f f 分;或者送到慢洗部,洗一块需 n天( n>m n > m ),其费用为 s s 分( s<f )。

每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,以及多少块保存起来延期送洗。但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。

试设计一个算法为餐厅合理地安排好 N N 天中餐巾使用计划,使总的花费最小。编程找出一个最佳餐巾使用计划。

输入输出格式

输入格式:

由标准输入提供输入数据。文件第 1 行有 1 1 个正整数 N ,代表要安排餐巾使用计划的天数。

接下来的 N N 行是餐厅在相继的 N 天里,每天需用的餐巾数。

最后一行包含 5 5 个正整数 p,m,f,n,s p p 是每块新餐巾的费用; m 是快洗部洗一块餐巾需用天数; f f 是快洗部洗一块餐巾需要的费用; n 是慢洗部洗一块餐巾需用天数; s s 是慢洗部洗一块餐巾需要的费用。

输出格式:

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

输入输出样例

输入样例#1:
3
1 7 5 
11 2 2 3 1
输出样例#1:
134

说明

N<=2000 N <= 2000

ri<=10000000 r i <= 10000000

p,f,s<=10000 p , f , s <= 10000

解题分析

费用流的经典模型。

考虑如何建边:我们显然不能像题目所述的一样, 将白天的点又连回晚上的点, 这样就会混淆退流边和表示餐巾清洗的边,并且费用加在哪里不好确定。

因此我们将图建成二分图模型,连以下 6 6 类边:

  • 源点S向每天白天连费用为 p p , 流量为INF的边, 表示每天白天可以买新的餐巾。

    • 源点 S S 向每天晚上连费用为0,流量为当天餐巾使用量的边, 表示每天需要使用的餐巾在晚上变成了旧餐巾,需要清洗。
    • 每天晚上向第二天晚上连费用为 0 0 ,流量为INF的边, 表示第一天的餐巾可以留到第二天。(其实还有一个作用:保证源点流入的流量始终和流出的流量平衡。如果我们不这样连的话即意味着第 i i 天的餐巾必须洗完后第i+m i+n i + n 天使用。当然也可以在每天早上向第二天早上连边, 效果相同。)
    • i i 天晚上向第i+m天早上连费用为 f f ,流量为INF的边, 表示第 i i 天晚上快洗的餐巾第i+m天早上可以使用。
    • i i 天晚上向第i+n天早上连费用为 f f ,流量为INF的边, 表示第 i i 天晚上慢洗的餐巾第i+n天早上可以使用。
    • 每天白天向汇点 T T 连费用为0,流量为当天餐巾使用量的边,表示每天白天需要这么多的餐巾。
    • 这样我们一路增广至最大流时即可满足。

      代码如下:

      #include <cstdio>
      #include <cmath>
      #include <cstring>
      #include <algorithm>
      #include <cstdlib>
      #include <cctype>
      #define R register
      #define IN inline
      #define W while
      #define gc getchar()
      #define MX 100050
      #define S 50000
      #define T 50001
      #define INF 999999999
      template <class TT>
      IN void in(TT &x)
      {
          x = 0; R char c = gc;
          W (!isdigit(c)) c = gc;
          W (isdigit(c))
          x = (x << 1) + (x << 3) + c - 48, c = gc;
      }
      struct Edge
      {
          int to, flow, cost, nex;
      }edge[MX << 1];
      int slow, fast, day, cnt = -1, nw, fc, sc, hd, tl;
      long long ans;
      int head[MX], rec[MX], del[MX], que[MX], dis[MX];
      bool inq[MX];
      IN void add(const int &from, const int &to, const int &cost, const int &fl)
      {
          edge[++cnt] = {to, fl, cost, head[from]}; head[from] = cnt;
          edge[++cnt] = {from, 0, -cost, head[to]}; head[to] = cnt;
      }
      IN bool SPFA()
      {
          R int now;
          que[tl = hd = 1] = S;
          std::memset(dis, 63, sizeof(dis));
          del[S] = INF; dis[S] = 0;
          W (tl <= hd)
          {
              now = que[tl++];
              for (R int i = head[now]; ~i; i = edge[i].nex)
              {
                  if(edge[i].flow > 0 && dis[edge[i].to] > dis[now] + edge[i].cost)
                  {
                      del[edge[i].to] = std::min(edge[i].flow, del[now]);
                      dis[edge[i].to] = dis[now] + edge[i].cost;
                      rec[edge[i].to] = i;
                      if(!inq[edge[i].to]) inq[edge[i].to] = true, que[++hd] = edge[i].to;
                  }
              }
              inq[now] = false;
          }
          return dis[T] < 99999999;
      }
      IN void updata()
      {
          ans += del[T] * dis[T];
          R int now = T, tar;
          W (now != S)
          {
              tar = rec[now];
              edge[tar].flow -= del[T];
              edge[tar ^ 1].flow += del[T];
              now = edge[tar ^ 1].to;
          }
      }
      long long EK()
      {
          W (SPFA()) updata();
          return ans;
      }
      int main(void)
      {
          in(day); int a, b;
          std::memset(head, -1, sizeof(head));
          for (R int i = 1; i <= day; ++i)
          in(a), add(i, T, 0, a), add(S, i + day, 0, a);
          in(nw), in(fast), in(fc), in(slow), in(sc);
          for (R int i = 1; i <= day; ++i) add(S, i, nw, INF);
          for (R int i = 1; i <  day; ++i) add(day + i, day + i + 1, 0, INF);
          int bd = std::max(0, day - fast);
          for (R int i = 1; i <=  bd; ++i) add(day + i, i + fast, fc, INF);
          bd = std::max(0, day - slow);
          for (R int i = 1; i <=  bd; ++i) add(day + i, i + slow, sc, INF);
          printf("%lld", EK());
      }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值