Skip to the content.

:warning: Project Selection Problem
(flow/project-selection-problem.hpp)

Project Selection Problem を最大流に帰着して解く.

扱える制約

Depends on

Code

#pragma once

#include "flow/max-flow.hpp"

template <class Cap = long long>
struct ProjectSelectionProblem {
  ProjectSelectionProblem() : ProjectSelectionProblem(numeric_limits<Cap>::max() / 4) {}
  explicit ProjectSelectionProblem(Cap inf) : n(2), sum(0), inf(inf) {}

  int add_project() { return n++; }
  void ban0(int x) { add_edge(x, 1, inf); }
  void lose0(int x, Cap w) { add_edge(x, 1, w); }
  void gain0(int x, Cap w) {
    sum += w;
    add_edge(0, x, w);
  }
  void ban1(int x) { add_edge(0, x, inf); }
  void lose1(int x, Cap w) { add_edge(0, x, w); }
  void gain1(int x, Cap w) {
    sum += w;
    add_edge(x, 1, w);
  }
  void ban01(int x, int y) { add_edge(x, y, inf); }
  void lose01(int x, int y, Cap w) { add_edge(x, y, w); }
  void gain_same(int x, int y, Cap w) {
    sum += w;
    add_edge(x, y, w);
    add_edge(y, x, w);
  }
  void lose_diff(int x, int y, Cap w) {
    add_edge(x, y, w);
    add_edge(y, x, w);
  }
  void ban_diff(int x, int y) {
    add_edge(x, y, inf);
    add_edge(y, x, inf);
  }
  void gain00(int x, int y, Cap w) {
    sum += w;
    int z = add_project();
    add_edge(0, z, w);
    add_edge(z, x, inf);
    add_edge(z, y, inf);
  }
  void gain11(int x, int y, Cap w) {
    sum += w;
    int z = add_project();
    add_edge(z, 1, w);
    add_edge(x, z, inf);
    add_edge(y, z, inf);
  }
  template <class Container>
  void gain_all0(const Container& xs, Cap w) {
    sum += w;
    int y = add_project();
    add_edge(0, y, w);
    for (int x : xs) add_edge(y, x, inf);
  }
  template <class Container>
  void gain_all1(const Container& xs, Cap w) {
    sum += w;
    int y = add_project();
    add_edge(y, 1, w);
    for (int x : xs) add_edge(x, y, inf);
  }
  Cap solve() const {
    MaxFlow<Cap> mf(n);
    for (auto [from, to, cap] : _edges) mf.add_edge(from, to, cap);
    return sum - mf.flow(0, 1);
  }

 private:
  void add_edge(int from, int to, Cap cap) {
    assert(0 <= from && from < n);
    assert(0 <= to && to < n);
    assert(0 <= cap);
    _edges.push_back({from, to, cap});
  }
  int n;
  Cap sum, inf;
  vector<tuple<int, int, Cap>> _edges;
};

/**
 * @brief Project Selection Problem
 * @docs docs/flow/project-selection-problem.md
 */
#line 2 "flow/project-selection-problem.hpp"

#line 2 "flow/max-flow.hpp"

#line 2 "data-structure/simple-queue.hpp"

template <class T>
struct SimpleQueue {
 private:
  vector<T> a;
  int p;

 public:
  SimpleQueue() : p(0) {}
  SimpleQueue(int n) : p(0) { a.reserve(n); }
  void reserve(int n) { a.reserve(n); }
  int size() { return a.size() - p; }
  bool empty() { return a.size() == p; }
  void push(const T& v) { a.push_back(v); }
  T& front() { return a[p]; }
  void pop() { p++; }
  void clear() {
    a.clear();
    p = 0;
  }
};
#line 4 "flow/max-flow.hpp"

template <class Cap = long long>
struct MaxFlow {
 public:
  MaxFlow() : _n(0) {}
  explicit MaxFlow(int n) : _n(n), g(n) {}

  int add_edge(int from, int to, Cap cap) {
    assert(0 <= from && from < _n);
    assert(0 <= to && to < _n);
    assert(0 <= cap);
    int m = int(pos.size());
    pos.push_back({from, int(g[from].size())});
    int from_id = int(g[from].size());
    int to_id = int(g[to].size());
    if (from == to) to_id++;
    g[from].push_back(_edge{to, to_id, cap});
    g[to].push_back(_edge{from, from_id, 0});
    return m;
  }

  struct edge {
    int from, to;
    Cap cap, flow;
  };

  edge get_edge(int i) {
    int m = int(pos.size());
    assert(0 <= i && i < m);
    auto _e = g[pos[i].first][pos[i].second];
    auto _re = g[_e.to][_e.rev];
    return edge{pos[i].first, _e.to, _e.cap + _re.cap, _re.cap};
  }
  vector<edge> edges() {
    int m = int(pos.size());
    vector<edge> result;
    for (int i = 0; i < m; i++) {
      result.push_back(get_edge(i));
    }
    return result;
  }
  void change_edge(int i, Cap new_cap, Cap new_flow) {
    int m = int(pos.size());
    assert(0 <= i && i < m);
    assert(0 <= new_flow && new_flow <= new_cap);
    auto& _e = g[pos[i].first][pos[i].second];
    auto& _re = g[_e.to][_e.rev];
    _e.cap = new_cap - new_flow;
    _re.cap = new_flow;
  }

  Cap flow(int s, int t) {
    return flow(s, t, numeric_limits<Cap>::max());
  }
  Cap flow(int s, int t, Cap flow_limit) {
    assert(0 <= s && s < _n);
    assert(0 <= t && t < _n);
    assert(s != t);

    vector<int> level(_n), iter(_n);
    SimpleQueue<int> que;

    auto bfs = [&]() {
      fill(level.begin(), level.end(), -1);
      level[s] = 0;
      que.clear();
      que.push(s);
      while (!que.empty()) {
        int v = que.front();
        que.pop();
        for (auto& e : g[v]) {
          if (e.cap == 0 || level[e.to] >= 0) continue;
          level[e.to] = level[v] + 1;
          if (e.to == t) return;
          que.push(e.to);
        }
      }
    };
    auto dfs = [&](auto self, int v, Cap up) {
      if (v == s) return up;
      Cap res = 0;
      int level_v = level[v];
      for (int& i = iter[v]; i < int(g[v].size()); i++) {
        _edge& e = g[v][i];
        if (level_v <= level[e.to] || g[e.to][e.rev].cap == 0) continue;
        Cap d = self(self, e.to, min(up - res, g[e.to][e.rev].cap));
        if (d <= 0) continue;
        g[v][i].cap += d;
        g[e.to][e.rev].cap -= d;
        res += d;
        if (res == up) return res;
      }
      level[v] = _n;
      return res;
    };

    Cap flow = 0;
    while (flow < flow_limit) {
      bfs();
      if (level[t] == -1) break;
      fill(iter.begin(), iter.end(), 0);
      Cap f = dfs(dfs, t, flow_limit - flow);
      if (!f) break;
      flow += f;
    }
    return flow;
  }

  vector<bool> min_cut(int s) {
    vector<bool> visited(_n);
    SimpleQueue<int> que;
    que.push(s);
    while (!que.empty()) {
      int p = que.front();
      que.pop();
      visited[p] = true;
      for (auto& e : g[p]) {
        if (e.cap && !visited[e.to]) {
          visited[e.to] = true;
          que.push(e.to);
        }
      }
    }
    return visited;
  }

 private:
  int _n;
  struct _edge {
    int to, rev;
    Cap cap;
  };
  vector<pair<int, int>> pos;
  vector<vector<_edge>> g;
};

/**
 * @brief Max Flow
 * @docs docs/flow/max-flow.md
 */
#line 4 "flow/project-selection-problem.hpp"

template <class Cap = long long>
struct ProjectSelectionProblem {
  ProjectSelectionProblem() : ProjectSelectionProblem(numeric_limits<Cap>::max() / 4) {}
  explicit ProjectSelectionProblem(Cap inf) : n(2), sum(0), inf(inf) {}

  int add_project() { return n++; }
  void ban0(int x) { add_edge(x, 1, inf); }
  void lose0(int x, Cap w) { add_edge(x, 1, w); }
  void gain0(int x, Cap w) {
    sum += w;
    add_edge(0, x, w);
  }
  void ban1(int x) { add_edge(0, x, inf); }
  void lose1(int x, Cap w) { add_edge(0, x, w); }
  void gain1(int x, Cap w) {
    sum += w;
    add_edge(x, 1, w);
  }
  void ban01(int x, int y) { add_edge(x, y, inf); }
  void lose01(int x, int y, Cap w) { add_edge(x, y, w); }
  void gain_same(int x, int y, Cap w) {
    sum += w;
    add_edge(x, y, w);
    add_edge(y, x, w);
  }
  void lose_diff(int x, int y, Cap w) {
    add_edge(x, y, w);
    add_edge(y, x, w);
  }
  void ban_diff(int x, int y) {
    add_edge(x, y, inf);
    add_edge(y, x, inf);
  }
  void gain00(int x, int y, Cap w) {
    sum += w;
    int z = add_project();
    add_edge(0, z, w);
    add_edge(z, x, inf);
    add_edge(z, y, inf);
  }
  void gain11(int x, int y, Cap w) {
    sum += w;
    int z = add_project();
    add_edge(z, 1, w);
    add_edge(x, z, inf);
    add_edge(y, z, inf);
  }
  template <class Container>
  void gain_all0(const Container& xs, Cap w) {
    sum += w;
    int y = add_project();
    add_edge(0, y, w);
    for (int x : xs) add_edge(y, x, inf);
  }
  template <class Container>
  void gain_all1(const Container& xs, Cap w) {
    sum += w;
    int y = add_project();
    add_edge(y, 1, w);
    for (int x : xs) add_edge(x, y, inf);
  }
  Cap solve() const {
    MaxFlow<Cap> mf(n);
    for (auto [from, to, cap] : _edges) mf.add_edge(from, to, cap);
    return sum - mf.flow(0, 1);
  }

 private:
  void add_edge(int from, int to, Cap cap) {
    assert(0 <= from && from < n);
    assert(0 <= to && to < n);
    assert(0 <= cap);
    _edges.push_back({from, to, cap});
  }
  int n;
  Cap sum, inf;
  vector<tuple<int, int, Cap>> _edges;
};

/**
 * @brief Project Selection Problem
 * @docs docs/flow/project-selection-problem.md
 */
Back to top page