zoukankan      html  css  js  c++  java
  • 题解 luogu P1251 【餐巾计划问题】

    题解 luogu P1251 【餐巾计划问题】

    时间:2019.3.19

    网络流24题:【餐巾计划问题】

    题目描述

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

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

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

    建图(1)

    考虑建出下面这张图:

    将每天当成一个点,根据当天的餐巾是否已使用,拆成
    \(S\)连边,费用为\(p\),表示可以任意买新餐巾
    连边,容量为\(r_i\),表示需要使用\(r_i\)条餐巾。
    \(N\)\(T\)连边,容量为\(\sum r_i\),表示需要将所有餐巾用掉。
    每天\(a\)天后的连边,费用为\(s\),表示送到快洗部(慢洗部同理)


    这样,我们只需要控制流向的流量均为\(r_i\)就行了。

    但是,这样就出现一个问题:我们没办法控制流向后的流量刚好流满呀?

    看来我们要改变建图方式了。

    建图(2)

    建出下面这张图:

    将每天当成一个点,根据当天的餐巾是否已使用,拆成
    \(S\)连边,费用为\(p\),表示可以任意买新餐巾。
    \(T\)连边,容量为\(r_i\)表示要消耗\(\textbf{r}_\textbf{i}\)条餐巾。
    \(S\)连边,容量为\(r_i\)表示收到用前产生的\(\textbf{r}_\textbf{i}\)条旧餐巾
    每天\(a\)天后的连边,费用为\(s\),表示送到快洗部(慢洗部同理)


    这样建图意味着的流与的流意义不同。
    \(S\)(或几天前的)流向的流表示能使用的新餐巾,
    而从\(S\)流向的流表示使用后留下的\(r_i\)条旧餐巾。

    我们控制了从流出的餐巾(即使用数量)为\(r_i\),而流进的餐巾数量刚好为\(r_i\)

    代码

    注意上面没有讲到的一点:使用前多余的干净餐巾可以留到明天再使用。
    将今天的向明天的连边即可。

    #include <bits/stdc++.h>
    using namespace std;
    #define int long long
    const int kMaxN = 2000 * 2 + 10;
    const int kMaxM = kMaxN * 4;
    const int kInf = 4e18;
    struct Graph {
      struct Arc {
        int to, cap, cost;
        int next;
      };
      Arc arcs[kMaxM << 1];
      int top, head[kMaxN];
      Graph() { top = 1; }
      void Add(int u, int v, int cap, int cost) {
        arcs[++top] = (Arc) {v, cap, cost, head[u]};
        head[u] = top;
        arcs[++top] = (Arc) {u, 0, -cost, head[v]};
        head[v] = top;
      }
      int s, t;
      queue<int> Q;
      bool in[kMaxN];
      int cost[kMaxN], flow[kMaxN], last[kMaxN];
      bool Spfa() {
        memset(flow, 0, sizeof(flow));
        memset(last, 0, sizeof(last));
        memset(cost, 0x7F, sizeof(cost));
        // 不用memset flow和last
        // 这两个数组不参与最短路计算
        // 一定要写flow[t] = 0
        // 如果有负环一定要写memset(in)
        Q.push(s);
        cost[s] = 0;
        flow[s] = kInf;
        flow[t] = 0; // !!!
        while (!Q.empty()) {
          int u = Q.front();
          Q.pop();
          in[u] = false;
          for (int i = head[u]; i; i = arcs[i].next) {
            Arc& arc = arcs[i];
            int v = arc.to;
            if (cost[u] + arc.cost < cost[v] && arc.cap) {
              cost[v] = cost[u] + arc.cost;
              flow[v] = min(flow[u], arc.cap);
              last[v] = i;
              if (!in[v]) {
                in[v] = true;
                Q.push(v);
              }
            }
          }
        }
        return flow[t] != 0;
      }
      int max_flow, min_cost;
      void Update() {
        int u = t;
        while (u != s) {
          int i = last[u];
          arcs[i].cap -= flow[t];
          arcs[i ^ 1].cap += flow[t];
          u = arcs[i ^ 1].to;
        }
        max_flow += flow[t];
        min_cost += cost[t] * flow[t];
      }
      void Ek() {
        max_flow = min_cost = 0;
        while (Spfa()) Update();
      }
    };
    Graph G;
    int n;
    int p, a, f, b, s;
    #define UNUSED(I) (I)
    #define USED(I) ((I) + 2001)
    signed main() {
      scanf("%lld", &n);
      G.s = kMaxN - 2;
      G.t = kMaxN - 1;
      for (int i = 1; i <= n; i++) {
        int r;
        scanf("%lld", &r);
        G.Add(UNUSED(i), G.t, r, 0);
        G.Add(G.s, USED(i), r, 0);
      }
      scanf("%lld %lld %lld %lld %lld",
            &p, &a, &f, &b, &s);
      for (int i = 1; i <= n; i++) {
        G.Add(G.s, UNUSED(i), kInf, p);
        if (i + 1 <= n) G.Add(UNUSED(i), UNUSED(i + 1), kInf, 0);
        if (i + a <= n) G.Add(USED(i), UNUSED(i + a), kInf, f);
        if (i + b <= n) G.Add(USED(i), UNUSED(i + b), kInf, s);
      }
      G.Ek();
      // printf("%lld %lld\n", G.min_cost, G.max_flow);
      printf("%lld\n", G.min_cost);
      return 0;
    }
    
  • 相关阅读:
    Use MVS Dsbame convensions. windows下ftp.exe客户端上传错误
    Sqlserver 2005:数据库快照
    Oracle:使用ASM自动存储管理, 严重推荐
    Thunderbird 邮件客户端:windows 和 ubuntu 或 liunx 下共用的方法
    Oracle:Oracle 10 RAC 安装群集件的准备工作
    SSH
    STL
    ASP生成静态Html文件技术杂谈
    Nessus:网络和主机漏洞评估程序安装试用
    table 的 id 属性不被 document.getElementById支持
  • 原文地址:https://www.cnblogs.com/longlongzhu123/p/10559539.html
Copyright © 2011-2022 走看看