結果

問題 No.957 植林
ユーザー qwewe
提出日時 2025-05-14 13:19:19
言語 C++17
(gcc 13.3.0 + boost 1.87.0)
結果
TLE  
実行時間 -
コード長 8,518 bytes
コンパイル時間 1,204 ms
コンパイル使用メモリ 89,948 KB
実行使用メモリ 28,980 KB
最終ジャッジ日時 2025-05-14 13:20:30
合計ジャッジ時間 7,026 ms
ジャッジサーバーID
(参考情報)
judge1 / judge2
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 3
other AC * 15 TLE * 1 -- * 29
権限があれば一括ダウンロードができます

ソースコード

diff #

#include <iostream>
#include <vector>
#include <queue>
#include <limits>
#include <numeric>

using namespace std;

// Use long long for capacities and flow values due to potentially large costs and gains (up to 10^9)
typedef long long ll;

// Define a large constant for infinite capacity edges.
// The maximum possible sum of finite capacities is roughly (H+W+HW) * 10^9.
// With H, W <= 300, this is about (300 + 300 + 300*300) * 10^9 = 90600 * 10^9 < 10^14.
// So 10^15 is a safe value for infinity.
const ll INF = 1e15; 

// Structure to represent an edge in the flow network graph
struct Edge {
    int to;          // The destination node index of the edge
    ll capacity;     // The current capacity of the edge
    int rev;         // The index of the reverse edge in the adjacency list of the 'to' node
                     // This is used to efficiently update residual capacities.
};

vector<vector<Edge>> graph; // Adjacency list representation of the graph
vector<int> level;          // Stores the level (distance from source) of each node in the BFS phase of Dinic's algorithm
vector<int> iter;           // Stores the current edge index being explored from each node in the DFS phase of Dinic's algorithm
                             // This helps avoid re-exploring edges that are already saturated or lead to dead ends in the current level graph.

// Function to add a directed edge and its corresponding reverse edge to the graph
// In the context of max flow, reverse edges are needed for the residual graph concept.
void add_edge(int u, int v, ll cap) {
    // Add the forward edge u -> v with the given capacity
    graph[u].push_back({v, cap, (int)graph[v].size()});
    // Add the reverse edge v -> u with initial capacity 0
    // This capacity will increase if flow is pushed back along the original edge u -> v
    graph[v].push_back({u, 0, (int)graph[u].size() - 1}); 
}

// Breadth-First Search (BFS) to build the level graph required by Dinic's algorithm
// It computes the shortest path distance (in terms of number of edges) from the source 's' to all reachable nodes.
bool bfs(int s, int t) {
    level.assign(graph.size(), -1); // Initialize all levels to -1 (unvisited)
    queue<int> q;
    level[s] = 0; // Level of the source node is 0
    q.push(s);
    while (!q.empty()) {
        int v = q.front();
        q.pop();
        // Explore neighbors of node v
        for (const auto& edge : graph[v]) {
            // If the edge has remaining capacity and the destination node hasn't been visited yet
            if (edge.capacity > 0 && level[edge.to] < 0) {
                level[edge.to] = level[v] + 1; // Assign level to the neighbor
                q.push(edge.to);
            }
        }
    }
    // Returns true if the sink 't' is reachable from the source 's', false otherwise
    // If sink is unreachable, no more augmenting paths can be found.
    return level[t] != -1;
}

// Depth-First Search (DFS) to find an augmenting path in the level graph and push flow along it
// It attempts to push 'f' units of flow from node 'v' to the sink 't'.
ll dfs(int v, int t, ll f) {
    // Base case: If we have reached the sink node, return the amount of flow 'f' that reached it.
    if (v == t) return f;
    
    // Iterate through the edges starting from the index stored in 'iter[v]'
    // This optimization prevents re-checking edges that have already been fully explored in the current DFS phase.
    for (int& i = iter[v]; i < graph[v].size(); ++i) {
        Edge& e = graph[v][i]; // Get a reference to the current edge
        // Check if the edge has remaining capacity and leads to a node in the next level of the level graph
        if (e.capacity > 0 && level[v] < level[e.to]) {
            // Recursively call DFS for the next node 'e.to'
            // The amount of flow to push is limited by the minimum of the remaining flow 'f' and the edge's capacity 'e.capacity'.
            ll d = dfs(e.to, t, min(f, e.capacity));
            // If flow 'd' was successfully pushed to the sink through this path (d > 0)
            if (d > 0) {
                e.capacity -= d; // Decrease the capacity of the forward edge
                graph[e.to][e.rev].capacity += d; // Increase the capacity of the reverse edge (in the residual graph)
                return d; // Return the amount of flow pushed
            }
        }
    }
    // If no augmenting path could be found from node 'v'
    return 0;
}

// Dinic's algorithm implementation to compute the maximum flow from source 's' to sink 't'
ll max_flow(int s, int t) {
    ll total_flow = 0; // Initialize total flow to 0
    // Repeat the process as long as BFS finds that the sink is reachable from the source (i.e., an augmenting path might exist)
    while (bfs(s, t)) {
        iter.assign(graph.size(), 0); // Reset the iterators for the DFS phase
        ll flow_increment;
        // Keep performing DFS to find augmenting paths and push flow along them until no more flow can be pushed in the current level graph
        while ((flow_increment = dfs(s, t, INF)) > 0) {
            total_flow += flow_increment; // Add the pushed flow to the total flow
        }
    }
    return total_flow; // Return the computed maximum flow
}

int main() {
    // Faster I/O operations
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int H, W; // Read grid dimensions: Height H, Width W
    cin >> H >> W;

    // Read the costs G[i][j] for planting a tree at cell (i, j)
    vector<vector<ll>> G(H, vector<ll>(W));
    for (int i = 0; i < H; ++i) {
        for (int j = 0; j < W; ++j) {
            cin >> G[i][j];
        }
    }

    // Read the row bonuses R[i]
    vector<ll> R(H);
    ll total_potential_gain = 0; // Initialize the total potential gain (sum of all bonuses)
    for (int i = 0; i < H; ++i) {
        cin >> R[i];
        total_potential_gain += R[i];
    }

    // Read the column bonuses C[j] and add them to the total potential gain
    vector<ll> C(W);
    for (int j = 0; j < W; ++j) {
        cin >> C[j];
        total_potential_gain += C[j];
    }

    // Calculate the total number of nodes required for the graph construction
    // Nodes: Source, Sink, H*W cell nodes, H row nodes, W column nodes
    int N_nodes = 2 + H * W + H + W; 
    int S = 0;         // Source node index is conventionally 0
    int T = N_nodes - 1; // Sink node index is the last index
    graph.resize(N_nodes); // Resize the adjacency list vector to accommodate all nodes

    // Helper lambda functions to map grid coordinates (0-based) and row/column indices to graph node indices
    // Cell (r, c) -> node index 1 + r*W + c (range [1, H*W])
    auto P_idx = [&](int r, int c) { return 1 + r * W + c; };
    // Row r -> node index 1 + H*W + r (range [1+H*W, H*W+H])
    auto ROW_idx = [&](int r) { return 1 + H * W + r; };
    // Column c -> node index 1 + H*W + H + c (range [1+H*W+H, H*W+H+W])
    auto COL_idx = [&](int c) { return 1 + H * W + H + c; };

    // Construct the graph edges according to the min-cut formulation:
    // 1. Edges from Source S to ROW_i nodes with capacity R[i]
    for (int i = 0; i < H; ++i) {
        add_edge(S, ROW_idx(i), R[i]);
    }

    // 2. Edges from Source S to COL_j nodes with capacity C[j]
    for (int j = 0; j < W; ++j) {
         add_edge(S, COL_idx(j), C[j]);
    }

    // 3. Edges from P_{i,j} nodes to Sink T with capacity G[i][j]
    for (int i = 0; i < H; ++i) {
        for (int j = 0; j < W; ++j) {
             add_edge(P_idx(i, j), T, G[i][j]);
        }
    }

    // 4. Infinite capacity edges from ROW_i nodes to corresponding P_{i,j} nodes (dependency constraint)
    for (int i = 0; i < H; ++i) {
        for (int j = 0; j < W; ++j) {
             add_edge(ROW_idx(i), P_idx(i, j), INF);
        }
    }

    // 5. Infinite capacity edges from COL_j nodes to corresponding P_{i,j} nodes (dependency constraint)
    for (int j = 0; j < W; ++j) {
         for (int i = 0; i < H; ++i) {
            add_edge(COL_idx(j), P_idx(i, j), INF);
         }
    }
    
    // Calculate the minimum cut capacity using the max-flow min-cut theorem.
    // The max flow value equals the min cut capacity.
    ll min_cut_cost = max_flow(S, T);

    // The maximum profit is derived from the min-cut formulation:
    // Max Profit = Total Potential Gain - Minimum Loss (Min Cut Cost)
    ll max_profit = total_potential_gain - min_cut_cost;

    // Output the calculated maximum profit
    cout << max_profit << endl;

    return 0;
}
0