标签: 最大权闭合子图

  • 洛谷3749 六省联考2017 寿司餐厅

    注意到每个权值只计算一次,考虑最大权闭合子图。

    每种代号新建一个点,权值为代号的平方乘m。

    每种寿司的权值为它本身的代号,向他代号对应的点连边。

    每个区间新建一个点,权值是他的代价,同时向这个区间的子区间和这个区间内的所有寿司连边。

    #include <algorithm>
    #include <iostream>
    #include <queue>
    #include <set>
    #include <vector>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t LARGE = 15010;
    const int_t SOURCE = 15001;
    const int_t SINK = 15002;
    const int_t OFFSET = 10000;
    const int_t OFFSET2 = 14000;
    const int_t INF = 0x7fffffff;
    struct Edge {
        int_t to, capacity, flow;
        Edge* rev;
    };
    std::vector<Edge*> graph[LARGE + 1];
    int_t dis[LARGE + 1], curr[LARGE + 1];
    bool visited[LARGE + 1];
    void pushEdge(int_t from, int_t to, int_t capacity) {
        Edge* edge0 = new Edge{to, capacity, 0};
        Edge* edge1 = new Edge{from, 0, 0, edge0};
        edge0->rev = edge1;
        graph[from].push_back(edge0);
        graph[to].push_back(edge1);
    #ifdef DEBUG
        cout << "Edge " << from << "," << to << "," << capacity << endl;
    #endif
    }
    bool BFS(int_t source, int_t sink) {
        std::fill(dis + 1, dis + 1 + LARGE, INF);
        std::fill(curr + 1, curr + 1 + LARGE, 0);
        std::fill(visited + 1, visited + 1 + LARGE, false);
        std::queue<int_t> queue;
        queue.push(source);
        dis[source] = 0;
        visited[source] = true;
        while (queue.empty() == false) {
            int_t front = queue.front();
            queue.pop();
            for (Edge* edge : graph[front]) {
                if (edge->capacity > edge->flow && visited[edge->to] == false) {
                    dis[edge->to] = dis[front] + 1;
                    visited[edge->to] = true;
                    queue.push(edge->to);
                }
            }
        }
    
        return visited[sink];
    }
    int_t DFS(int_t vtx, int_t sink, int_t minf = INF) {
        if (minf == 0 || vtx == sink) return minf;
        int_t result = 0;
        for (int_t& i = curr[vtx]; i < graph[vtx].size(); i++) {
            Edge* edge = graph[vtx][i];
            if (dis[edge->to] == dis[vtx] + 1) {
                int_t x = DFS(edge->to, sink,
                              std::min(minf, edge->capacity - edge->flow));
                if (x > 0) {
                    minf -= x;
                    result += x;
                    edge->flow += x;
                    edge->rev->flow -= x;
                    if (minf == 0) break;
                }
            }
        }
        return result;
    }
    int main() {
        int_t n, m;
        cin >> n >> m;
        int_t result = 0;
        std::set<int_t> numbers;
        for (int_t i = 1; i <= n; i++) {
            int_t x;
            cin >> x;
            numbers.insert(x);
            pushEdge(OFFSET2 + i, SINK, x);
            pushEdge(OFFSET2 + i, OFFSET + x, INF);
            // result += x;
        }
        for (int_t x : numbers) {
            // result += x * x * m;
            pushEdge(OFFSET + x, SINK, x * x * m);
        }
        const auto encode = [&](int_t r, int_t c) -> int_t {
            return (r - 1) * n + c;
        };
        for (int_t i = 1; i <= n; i++) {
            for (int_t j = i; j <= n; j++) {
                int_t x;
                cin >> x;
                if (x > 0) {
                    result += x;
                    pushEdge(SOURCE, encode(i, j), x);
                } else {
                    pushEdge(encode(i, j), SINK, -x);
                }
                if (i != j) {
                    pushEdge(encode(i, j), encode(i + 1, j), INF);
                    pushEdge(encode(i, j), encode(i, j - 1), INF);
                }
                for (int_t k = i; k <= j; k++) {
                    pushEdge(encode(i, j), OFFSET2 + k, INF);
                }
            }
        }
        while (BFS(SOURCE, SINK)) {
            int_t x = DFS(SOURCE, SINK);
    #ifdef DEBUG
            cout << "flow " << x << endl;
    #endif
            result -= x;
        }
        cout << result << endl;
        return 0;
    }
    

     

  • 洛谷2762 太空飞行计划问题

    最大权闭合子图。

    输出方案?

    注意到所有割边要么和源点相连,要么和汇点相连。

    故从源点开始BFS,能走到的点全都在源点集合内。

    #include <algorithm>
    #include <cstring>
    #include <iostream>
    #include <queue>
    #include <vector>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t LARGE = 210;
    const int_t SOURCE = 201;
    const int_t SINK = 202;
    const int_t OFFSET = 100;
    const int_t INF = 0x7ffffffff;
    struct Edge {
        int_t to, capacity, flow;
        Edge* rev;
    };
    std::vector<Edge*> graph[LARGE + 1];
    Edge* pushEdge(int_t from, int_t to, int_t capacity) {
        Edge* edge0 = new Edge{to, capacity, 0};
        Edge* edge1 = new Edge{from, 0, 0, edge0};
        edge0->rev = edge1;
        graph[from].push_back(edge0);
        graph[to].push_back(edge1);
    #ifdef DEBUG
        cout << "pushedge " << from << "," << to << " " << capacity << endl;
    #endif
        return edge0;
    }
    
    int_t dis[LARGE + 1];
    bool visited[LARGE + 1];
    int_t curr[LARGE + 1];
    bool BFS(int_t source, int_t sink) {
        std::queue<int_t> queue;
        std::fill(dis, dis + 1 + LARGE, INF);
        std::fill(visited, visited + 1 + LARGE, false);
        std::fill(curr, curr + 1 + LARGE, 0);
        dis[source] = 0;
        queue.push(source);
        visited[source] = true;
        while (queue.empty() == false) {
            int_t front = queue.front();
            queue.pop();
            visited[front] = true;
            for (Edge* edge : graph[front]) {
                if (edge->capacity > edge->flow && visited[edge->to] == false) {
                    dis[edge->to] = dis[front] + 1;
                    visited[edge->to] = true;
                    queue.push(edge->to);
                }
            }
        }
        return visited[sink];
    }
    int_t DFS(int_t vtx, int_t sink, int_t minf = INF) {
        if (vtx == sink || minf == 0) return minf;
        int_t result = 0;
        for (int_t& i = curr[vtx]; i < graph[vtx].size(); i++) {
            Edge* edge = graph[vtx][i];
            if (dis[edge->to] == dis[vtx] + 1) {
                int_t x = DFS(edge->to, sink,
                              std::min(minf, edge->capacity - edge->flow));
                if (x > 0) {
                    result += x;
                    minf -= x;
                    edge->flow += x;
                    edge->rev->flow -= x;
                    if (minf == 0) break;
                }
            }
        }
        return result;
    }
    int_t n, m;
    
    int main() {
        cin >> n >> m;
        int_t result = 0;
        for (int_t i = 1; i <= n; i++) {
            int_t val;
            cin >> val;
            result += val;
            static char buf[1024];
            cin.getline(buf, 1024);
            pushEdge(SOURCE, i, val);
            char* str = strtok(buf, " ");
            do {
                if (str != nullptr) {
                    pushEdge(i, atoi(str) + OFFSET, INF);
                }
            } while ((str = strtok(NULL, " ")));
        }
        for (int_t i = 1; i <= m; i++) {
            int_t val;
            cin >> val;
            pushEdge(OFFSET + i, SINK, val);
        }
        while (BFS(SOURCE, SINK)) result -= DFS(SOURCE, SINK);
        std::vector<int_t> usedItems, usedExprs;
        std::queue<int_t> queue;
        static bool visited[LARGE + 1];
        queue.push(SOURCE);
        visited[SOURCE] = true;
        while (queue.empty() == false) {
            int_t front = queue.front();
            if (front != SOURCE) {
                if (front <= OFFSET)
                    usedExprs.push_back(front);
                else if (front > OFFSET)
                    usedItems.push_back(front - OFFSET);
            }
    
            queue.pop();
    #ifdef DEBUG
            cout << "BFS at " << front << endl;
    #endif
            for (Edge* edge : graph[front]) {
                if (edge->capacity > edge->flow && visited[edge->to] == false) {
                    visited[edge->to] = true;
                    queue.push(edge->to);
                }
            }
        }
        for (int_t x : usedExprs) cout << x << " ";
        cout << endl;
        for (int_t x : usedItems) cout << x << " ";
        cout << endl;
        cout << result << endl;
        return 0;
    }

     

  • NOI2009 植物大战僵尸

    最大权闭合子图。

    这是啥?

    给一张DAG,点有点权,要求选择一个点就要选择他的所有后继,要求找一个最大的子图。

    这题?

    一个植物如果要被干掉,那么首先要干掉保护他的植物。

    这个关系可以用一条有向边来描述。

    但是这不是个DAG啊。

    如果有植物互相保护,那么这些植物就无敌了。

    所有要先搞出来SCC,以及由这个SCC保护的植物。

    #include <algorithm>
    #include <iostream>
    #include <queue>
    #include <vector>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    const int_t LARGE = 1000;
    const int_t SOURCE = 990;
    const int_t SINK = 991;
    const int_t INF = 0x7fffffff;
    struct Edge {
        int_t from;
        int_t to;
        int_t capacity;
        int_t flow = 0;
        Edge* rev;
        bool isRev;
        Edge(int_t from, int_t to, int_t capacity) {
            this->from = from;
            this->to = to;
            this->capacity = capacity;
        }
    };
    std::vector<Edge*> graph[LARGE + 1];
    int_t dis[LARGE + 1];
    int_t curr[LARGE + 1];
    bool visited[LARGE + 1];
    bool onSCC[LARGE + 1];
    int_t vals[LARGE + 1];
    int_t inDeg[LARGE + 1];
    int_t n, m;
    int_t sum = 0;
    int_t encode(int_t r, int_t c) { return r * m + c + 1; }
    void pushEdge(int_t from, int_t to, int_t cap) {
        Edge* e0 = new Edge(from, to, cap);
        e0->isRev = false;
        Edge* e1 = new Edge(to, from, 0);
        e1->isRev = true;
        e1->rev = e0;
        e0->rev = e1;
        graph[from].push_back(e0);
        graph[to].push_back(e1);
    #ifdef DEBUG
        if (cap == INF) cout << from << " " << to << endl;
            // cout << "push edge " << from << "," << to << "," << cap << endl;
    #endif
    }
    
    namespace network {
    
    bool BFS() {
        std::fill(dis + 1, dis + 1 + LARGE, INF);
        std::fill(visited + 1, visited + 1 + LARGE, false);
        std::fill(curr + 1, curr + 1 + LARGE, 0);
        std::queue<int_t> queue;
        dis[SOURCE] = 0;
        queue.push(SOURCE);
        visited[SOURCE] = true;
        while (queue.empty() == false) {
            int_t front = queue.front();
            queue.pop();
            for (auto edge : graph[front]) {
                if (edge->capacity > edge->flow && visited[edge->to] == false &&
                    onSCC[edge->to] == false) {
                    dis[edge->to] = dis[front] + 1;
                    visited[edge->to] = true;
                    queue.push(edge->to);
                }
            }
        }
        return visited[SINK];
    }
    int_t DFS(int_t vtx, int_t minf = INF) {
        if (minf == 0 || vtx == SINK) return minf;
        int_t result = 0;
        if (onSCC[vtx]) return 0;
        for (int_t& i = curr[vtx]; i < graph[vtx].size(); i++) {
            auto edge = graph[vtx][i];
            if (dis[edge->to] == dis[edge->from] + 1) {
                int_t curr =
                    DFS(edge->to, std::min(minf, edge->capacity - edge->flow));
                if (curr > 0) {
                    minf -= curr;
                    result += curr;
                    edge->flow += curr;
                    edge->rev->flow -= curr;
                    if (minf == 0) break;
                }
            }
        }
        return result;
    }
    int_t execute() {
        int_t result = 0;
        while (BFS()) {
            result += DFS(SOURCE);
    #ifdef DEBUG
            cout << "arg to " << result << endl;
    #endif
        }
        return result;
    }
    }  // namespace network
    
    int main() {
        cin >> n >> m;
        for (int_t i = 0; i < n; i++) {
            for (int_t j = 0; j < m; j++) {
                int_t val, attacks;
                cin >> val >> attacks;
                if (j != m - 1) {
                    pushEdge(encode(i, j), encode(i, j + 1), INF);
                    inDeg[encode(i, j)] += 1;
                }
                for (int_t k = 1; k <= attacks; k++) {
                    int_t r, c;
                    cin >> r >> c;
                    pushEdge(encode(r, c), encode(i, j), INF);
                    inDeg[encode(r, c)] += 1;
                }
                vals[encode(i, j)] = val;
            }
        }
        std::queue<int_t> queue;
        for (int_t i = 1; i <= n * m; i++) {
            if (inDeg[i] == 0) queue.push(i);
            onSCC[i] = true;
        }
        while (queue.empty() == false) {
            int_t front = queue.front();
            queue.pop();
            onSCC[front] = false;
            for (auto edge : graph[front]) {
                if (edge->isRev) {
                    inDeg[edge->to]--;
                    if (inDeg[edge->to] == 0) queue.push(edge->to);
                }
            }
        }
    #ifdef DEBUG
        for (int_t i = 1; i <= n * m; i++) {
            cout << "onSCC " << i << " = " << onSCC[i] << endl;
        }
    #endif
        for (int_t i = 0; i < n; i++) {
            for (int_t j = 0; j < m; j++) {
                int_t val = vals[encode(i, j)];
                if (val > 0) {
                    pushEdge(SOURCE, encode(i, j), val);
                    if (onSCC[encode(i, j)] == false) sum += val;
                } else {
                    pushEdge(encode(i, j), SINK, -val);
                }
            }
        }
    
    #ifdef DEBUG
    
        cout << "sum = " << sum << endl;
    #endif
        cout << sum - network::execute() << endl;
        return 0;
    }