結果
問題 |
No.957 植林
|
ユーザー |
![]() |
提出日時 | 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 |
ソースコード
#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; }