firiexp's Library

This documentation is automatically generated by online-judge-tools/verification-helper

View the Project on GitHub firiexp/library

:heavy_check_mark: Project Selection Problem
(flow/project_selection_problem.cpp)

説明

二値変数 x_i in {0, 1} に対する project selection problem を最大流で解く。 扱えるのは次の形の利益の和の最大化。

x_u = 1 -> x_v = 1 の依存制約もこの特別ケースとして表せる。

できること

使い方

各利益と制約を追加してから solve() を呼ぶ。 行や列、区間などのボーナスを表す補助頂点が必要なら add_vertex() で増やす。

実装上の補足

正の利益を始点側、負の利益を終点側に張り、x=1, y=0 の罰金を x -> y の辺に乗せる最大重み閉包として解く。 get_selected() は最小カット後に始点側へ残った頂点集合を返す。

Depends on

Verified with

Code

#include "./dinic.cpp"

template<class T>
class ProjectSelectionProblem {
    int n;
    T base_score{};
    vector<T> weight;
    vector<tuple<int, int, T>> penalty;
    vector<int> selected;

public:
    ProjectSelectionProblem() : n(0) {}
    explicit ProjectSelectionProblem(int n) : n(n), base_score(0), weight(n, 0), selected(n, 0) {}

    int add_vertex() {
        weight.emplace_back(0);
        selected.emplace_back(0);
        return n++;
    }

    int size() const {
        return n;
    }

    void add_true_profit(int v, T x) {
        weight[v] += x;
    }

    void add_false_profit(int v, T x) {
        base_score += x;
        weight[v] -= x;
    }

    void add_penalty(int x, int y, T cost) {
        penalty.emplace_back(x, y, cost);
    }

    void add_if_then(int x, int y) {
        add_penalty(x, y, INF<T>);
    }

    void force_true(int v) {
        add_true_profit(v, INF<T>);
    }

    void force_false(int v) {
        add_false_profit(v, INF<T>);
    }

    T solve() {
        int s = n, t = n + 1;
        Dinic<T, true> mf(n + 2);
        T offset = base_score;
        for (int v = 0; v < n; ++v) {
            if (weight[v] >= 0) {
                offset += weight[v];
                mf.add_edge(s, v, weight[v]);
            } else {
                mf.add_edge(v, t, -weight[v]);
            }
        }
        for (auto&& [x, y, cost] : penalty) {
            mf.add_edge(x, y, cost);
        }
        T cut = mf.flow(s, t);

        fill(selected.begin(), selected.end(), 0);
        queue<int> q;
        q.emplace(s);
        vector<int> vis(n + 2, 0);
        vis[s] = 1;
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            for (auto&& e : mf.G[v]) {
                if (e.cap <= 0 || vis[e.to]) continue;
                vis[e.to] = 1;
                q.emplace(e.to);
            }
        }
        for (int v = 0; v < n; ++v) {
            selected[v] = vis[v];
        }
        return offset - cut;
    }

    const vector<int>& get_selected() const {
        return selected;
    }
};

/**
 * @brief Project Selection Problem
 */
#line 1 "flow/dinic.cpp"
template<class T, bool directed>
class Dinic {
    void bfs(int s){
        fill(level.begin(),level.end(), -1);
        queue<int> Q;
        level[s] = 0;
        Q.emplace(s);
        while(!Q.empty()){
            int v = Q.front(); Q.pop();
            for (auto &&e : G[v]){
                if(e.cap > 0 && level[e.to] < 0){
                    level[e.to] = level[v] + 1;
                    Q.emplace(e.to);
                }
            }
        }
    }
 
    T dfs(int v, int t, T f){
        if(v == t) return f;
        for(int &i = iter[v]; i < G[v].size(); i++){
            edge &e = G[v][i];
            if(e.cap > 0 && level[v] < level[e.to]){
                T d = dfs(e.to, t, min(f,  e.cap));
                if(d == 0) continue;
                e.cap -= d;
                G[e.to][e.rev].cap += d;
                return d;
            }
        }
        return 0;
    }
public:
    struct edge {
        int to{}; T cap; int rev{};
        edge() = default;
        edge(int to, T cap, int rev) : to(to), cap(cap), rev(rev) {}
    };
 
    vector<vector<edge>> G;
    vector<int> level, iter;
    Dinic() = default;
    explicit Dinic(int n) : G(n), level(n), iter(n) {}
 
    void add_edge(int from, int to, T cap){
        G[from].emplace_back(to, cap, G[to].size());
        G[to].emplace_back(from, directed ? 0 : cap,  G[from].size()-1);
    }
 
 
    T flow(int s, int t, T lim = INF<T>){
        T ret = 0;
        while(true) {
            bfs(s);
            if(level[t] < 0 || lim == 0) break;
            fill(iter.begin(),iter.end(), 0);
            while(true){
                T f = dfs(s, t, lim);
                if(f == 0) break;
                ret += f;
                lim -= f;
            }
        }
        return ret;
    }
};

/**
 * @brief Dinic法(Dinic)
 */
#line 2 "flow/project_selection_problem.cpp"

template<class T>
class ProjectSelectionProblem {
    int n;
    T base_score{};
    vector<T> weight;
    vector<tuple<int, int, T>> penalty;
    vector<int> selected;

public:
    ProjectSelectionProblem() : n(0) {}
    explicit ProjectSelectionProblem(int n) : n(n), base_score(0), weight(n, 0), selected(n, 0) {}

    int add_vertex() {
        weight.emplace_back(0);
        selected.emplace_back(0);
        return n++;
    }

    int size() const {
        return n;
    }

    void add_true_profit(int v, T x) {
        weight[v] += x;
    }

    void add_false_profit(int v, T x) {
        base_score += x;
        weight[v] -= x;
    }

    void add_penalty(int x, int y, T cost) {
        penalty.emplace_back(x, y, cost);
    }

    void add_if_then(int x, int y) {
        add_penalty(x, y, INF<T>);
    }

    void force_true(int v) {
        add_true_profit(v, INF<T>);
    }

    void force_false(int v) {
        add_false_profit(v, INF<T>);
    }

    T solve() {
        int s = n, t = n + 1;
        Dinic<T, true> mf(n + 2);
        T offset = base_score;
        for (int v = 0; v < n; ++v) {
            if (weight[v] >= 0) {
                offset += weight[v];
                mf.add_edge(s, v, weight[v]);
            } else {
                mf.add_edge(v, t, -weight[v]);
            }
        }
        for (auto&& [x, y, cost] : penalty) {
            mf.add_edge(x, y, cost);
        }
        T cut = mf.flow(s, t);

        fill(selected.begin(), selected.end(), 0);
        queue<int> q;
        q.emplace(s);
        vector<int> vis(n + 2, 0);
        vis[s] = 1;
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            for (auto&& e : mf.G[v]) {
                if (e.cap <= 0 || vis[e.to]) continue;
                vis[e.to] = 1;
                q.emplace(e.to);
            }
        }
        for (int v = 0; v < n; ++v) {
            selected[v] = vis[v];
        }
        return offset - cut;
    }

    const vector<int>& get_selected() const {
        return selected;
    }
};

/**
 * @brief Project Selection Problem
 */
Back to top page