結果
問題 |
No.1324 Approximate the Matrix
|
ユーザー |
![]() |
提出日時 | 2025-05-14 13:19:39 |
言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
結果 |
AC
|
実行時間 | 167 ms / 2,000 ms |
コード長 | 11,532 bytes |
コンパイル時間 | 1,389 ms |
コンパイル使用メモリ | 99,796 KB |
実行使用メモリ | 7,844 KB |
最終ジャッジ日時 | 2025-05-14 13:20:51 |
合計ジャッジ時間 | 4,702 ms |
ジャッジサーバーID (参考情報) |
judge5 / judge4 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | AC * 3 |
other | AC * 42 |
ソースコード
#include <iostream> #include <vector> #include <numeric> #include <queue> #include <limits> using namespace std; // Define long long for larger integer types typedef long long ll; // Define a large constant for infinity, carefully chosen to avoid overflow during additions const ll INF = numeric_limits<ll>::max() / 3; // Structure to represent edges in the graph struct Edge { int to; // Target node index int rev; // Index of the reverse edge in the adjacency list of the target node G[to] ll cap; // Original capacity of the edge ll flow; // Current flow on the edge int P; // P_ij value associated with the edge (for convex cost calculation), -1 if not applicable bool is_convex; // Flag indicating if the edge has a convex cost function (P_ij - x)^2 }; vector<vector<Edge>> G; // Adjacency list representation of the graph vector<ll> h; // Potentials for nodes (used in Dijkstra with potentials) vector<ll> dist; // Distances from the source node in Dijkstra's algorithm vector<int> prev_v; // Stores the previous vertex in the shortest path found by Dijkstra vector<int> prev_e; // Stores the index of the edge used to reach the current vertex from prev_v vector<bool> prev_fwd; // Stores whether the edge used was in the forward direction int N_nodes; // Total number of nodes in the graph // Function to add an edge and its corresponding reverse edge to the graph // u, v: source and target nodes // cap: capacity of the edge // P_val: parameter P_ij for convex cost calculation. If -1, edge has 0 cost. void add_edge(int u, int v, ll cap, int P_val = -1) { bool convex = (P_val != -1); // Determine if the edge has convex cost // Add the forward edge u -> v G[u].push_back({v, (int)G[v].size(), cap, 0, P_val, convex}); // Add the reverse edge v -> u. Initial capacity is 0. P_val is copied for consistency. G[v].push_back({u, (int)G[u].size() - 1, 0, 0, P_val, convex}); } // Function to compute the minimum cost flow using the successive shortest path algorithm (Primal-Dual) // adapted for convex quadratic costs of the form (P_ij - x)^2. // s: source node index // t: sink node index // K: total required flow // Returns the sum of marginal costs accumulated, which equals Sum(c(Q_ij) - c(0)) ll min_cost_flow(int s, int t, ll K) { ll total_algo_cost = 0; // Accumulates the sum of marginal costs for the flow pushed h.assign(N_nodes, 0); // Initialize potentials to 0 for all nodes ll current_total_flow = 0; // Tracks the total flow pushed so far prev_fwd.resize(N_nodes); // Resize the path reconstruction helper vector // Loop until the required total flow K is reached while (current_total_flow < K) { dist.assign(N_nodes, INF); // Initialize distances to infinity dist[s] = 0; // Distance from source to itself is 0 prev_v.assign(N_nodes, -1); // Reset path reconstruction info prev_e.assign(N_nodes, -1); // Use a min-priority queue for Dijkstra's algorithm priority_queue<pair<ll, int>, vector<pair<ll, int>>, greater<pair<ll, int>>> pq; pq.push({0, s}); // Push the source node with distance 0 // Dijkstra's algorithm main loop while (!pq.empty()) { ll d = pq.top().first; // Current shortest distance found int u = pq.top().second; // Node with the current shortest distance pq.pop(); // If we found a shorter path to u already, skip this element if (d > dist[u]) continue; // Explore edges starting from node u for (int i = 0; i < G[u].size(); ++i) { Edge &e = G[u][i]; // Current edge u -> v int v = e.to; // Try relaxing using the forward edge u -> v in the residual graph if (e.cap - e.flow > 0) { // Check if there is residual capacity ll marginal_cost; // Calculate the marginal cost for pushing 1 unit of flow if (e.is_convex) { // Marginal cost for convex quadratic cost (P-x)^2 is 1 - 2P + 2x marginal_cost = 1 - 2LL * e.P + 2LL * e.flow; } else { // For edges with 0 cost function (e.g., source/sink connections), marginal cost is 0 marginal_cost = 0; } // Calculate the reduced cost using potentials: cost' = cost + h[u] - h[v] ll reduced_cost = marginal_cost + h[u] - h[v]; // If a shorter path to v is found via u using this edge forward if (dist[u] != INF && dist[u] + reduced_cost < dist[v]) { dist[v] = dist[u] + reduced_cost; // Update distance prev_v[v] = u; // Record predecessor vertex prev_e[v] = i; // Record index of edge e in G[u] prev_fwd[v] = true; // Mark that this edge was used in the forward direction pq.push({dist[v], v}); // Push updated distance to priority queue } } // Try relaxing using the backward edge u <- v in the residual graph // This corresponds to reducing flow on the original edge v -> u Edge &rev_e = G[v][e.rev]; // Get the original edge v -> u if (rev_e.flow > 0) { // Check if there is flow on v -> u that can be pushed back ll marginal_cost_backward; // Calculate the cost of the backward edge u -> v in the residual graph if (rev_e.is_convex) { // Cost of pushing back flow x on edge (v -> u) with parameter P is 2P - 2x + 1 marginal_cost_backward = 1 + 2LL * rev_e.P - 2LL * rev_e.flow; } else { marginal_cost_backward = 0; // Cost is 0 for non-convex edges } // Calculate reduced cost for the backward edge u -> v ll reduced_cost_backward = marginal_cost_backward + h[u] - h[v]; // If a shorter path to v is found via u using this edge backward if (dist[u] != INF && dist[u] + reduced_cost_backward < dist[v]) { dist[v] = dist[u] + reduced_cost_backward; // Update distance prev_v[v] = u; // Record predecessor vertex prev_e[v] = i; // Store index of edge e=(u,v). Path reconstruction uses this to find the reverse edge. prev_fwd[v] = false; // Mark that this edge was used in the backward direction pq.push({dist[v], v}); // Push updated distance } } } } // If the sink t is unreachable, it means we cannot push more flow. Break the loop. // Based on problem statement G is non-empty, this should only happen if K=0 or if K is achieved. if (dist[t] == INF) { break; } // Update potentials h[v] for all nodes using the shortest path distances found by Dijkstra // h[v] = h[v] + dist[v]. This ensures reduced costs remain non-negative. for (int i = 0; i < N_nodes; ++i) { if (dist[i] != INF) { // Only update potential if node is reachable h[i] += dist[i]; } } // Augment flow by 1 unit along the found shortest path ll flow_to_push = 1; current_total_flow += flow_to_push; // Backtrack from sink t to source s to update flows and calculate the cost contribution of this path int curr = t; while (curr != s) { int pv = prev_v[curr]; // Predecessor vertex int edge_idx = prev_e[curr]; // Index of edge from pv used to reach curr bool fwd = prev_fwd[curr]; // Was the edge used forward? if (fwd) { // Edge pv -> curr was used forward Edge &e = G[pv][edge_idx]; // The edge pv -> curr // Calculate marginal cost based on flow *before* augmentation ll marginal_cost = e.is_convex ? (1 - 2LL * e.P + 2LL * e.flow) : 0; total_algo_cost += marginal_cost; // Add marginal cost to total cost e.flow += flow_to_push; // Augment flow on the forward edge } else { // Edge pv -> curr was used backward (meaning flow on curr -> pv was reduced) Edge &rev_e = G[curr][G[pv][edge_idx].rev]; // The edge curr -> pv // Calculate marginal cost for backward edge based on flow *before* augmentation ll marginal_cost_backward = rev_e.is_convex ? (1 + 2LL * rev_e.P - 2LL * rev_e.flow) : 0; total_algo_cost += marginal_cost_backward; // Add marginal cost to total cost rev_e.flow -= flow_to_push; // Decrease flow on the edge curr -> pv } curr = pv; // Move to the previous node in the path } } // The algorithm computes Sum(c(Q_ij) - c(0)). Return this value. return total_algo_cost; } int main() { ios_base::sync_with_stdio(false); // Faster I/O cin.tie(NULL); int N; // Matrix size N x N ll K; // Total flow required cin >> N >> K; vector<ll> A(N), B(N); // Row sums A and column sums B for (int i = 0; i < N; ++i) cin >> A[i]; for (int i = 0; i < N; ++i) cin >> B[i]; vector<vector<int>> P(N, vector<int>(N)); // Given matrix P ll P_sq_sum = 0; // Calculate sum of squares of P elements, Sum P_ij^2 for (int i = 0; i < N; ++i) { for (int j = 0; j < N; ++j) { cin >> P[i][j]; P_sq_sum += (ll)P[i][j] * P[i][j]; } } // Setup the graph for min-cost flow problem N_nodes = 2 * N + 2; // Total nodes: Source(1) + Rows(N) + Cols(N) + Sink(1) int s = 0, t = 2 * N + 1; // Source node index 0, Sink node index 2N+1 G.assign(N_nodes, vector<Edge>()); // Initialize graph adjacency list // Add edges from Source s=0 to Row nodes i+1 (indices 1 to N) for (int i = 0; i < N; ++i) { if (A[i] > 0) { // Optimization: only add edge if capacity A_i > 0 add_edge(s, i + 1, A[i]); // Capacity A_i, cost 0 (non-convex) } } // Add edges from Column nodes N+1+j (indices N+1 to 2N) to Sink t=2N+1 for (int j = 0; j < N; ++j) { if (B[j] > 0) { // Optimization: only add edge if capacity B_j > 0 add_edge(N + 1 + j, t, B[j]); // Capacity B_j, cost 0 (non-convex) } } // Add edges from Row nodes i+1 to Column nodes N+1+j for (int i = 0; i < N; ++i) { for (int j = 0; j < N; ++j) { // Capacity K is a safe upper bound on flow Q_ij. add_edge(i + 1, N + 1 + j, K, P[i][j]); // Capacity K, convex cost based on P_ij } } // Compute the minimum cost flow value which corresponds to Sum(c(Q_ij) - c(0)) ll min_cost_algo = min_cost_flow(s, t, K); // The final answer is the minimum value of Sum (P_ij - Q_ij)^2 = Sum c(Q_ij) // This equals min_cost_algo + Sum c(0) = min_cost_algo + P_sq_sum cout << min_cost_algo + P_sq_sum << endl; return 0; }