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;
    }
    
  • 相关阅读:
    蓝桥网试题 java 基础练习 特殊的数字
    蓝桥网试题 java 基础练习 杨辉三角形
    蓝桥网试题 java 基础练习 查找整数
    蓝桥网试题 java 基础练习 数列特征
    蓝桥网试题 java 基础练习 字母图形
    蓝桥网试题 java 基础练习 01字串
    蓝桥网试题 java 基础练习 回文数
    蓝桥网试题 java 基础练习 特殊回文数
    Using text search in Web page with Sikuli
    each of which 用法
  • 原文地址:https://www.cnblogs.com/longlongzhu123/p/10559539.html
Copyright © 2011-2022 走看看