Path Sum: Three Ways
In the same 80x80 matrix, find the minimal path sum from any cell in the left column to any cell in the right column, where at each step you may move right, up, or down (but not left).
Problem Statement
This archive keeps the full statement, math, and original media on the page.
NOTE: This problem is a more challenging version of Problem 81.
The minimal path sum in the
Find the minimal path sum from the left column to the right column in matrix.txt (right click and "Save Link/Target As..."), a 31K text file containing an
Problem 82: Path Sum: Three Ways
Mathematical Foundation
Let be an matrix with positive integer entries. We seek:
where a valid path starts at any , ends at any , and uses only moves , , or .
Define as the minimal cost to reach cell from any cell in column 0.
Lemma 1 (No vertical reversal). In any optimal path, the portion within a single column is a monotone vertical segment (purely upward or purely downward). That is, no optimal path goes up and then down (or down and then up) within the same column. Proof. Suppose an optimal path within column visits cells at rows in that order, and there exist indices such that (a down-then-up pattern). Then the path visits some cell and later revisits a row between and . Since all matrix entries are positive, the cost of the detour through the intermediate cells is strictly positive. Removing the detour (going directly from to via the shorter vertical path) yields a cheaper path, contradicting optimality. The argument for up-then-down is symmetric.
Theorem 1 (Two-pass correctness). The following column-by-column algorithm correctly computes for all :
Initialization: for all .
For each column :
- Set for all .
- Downward pass: For : .
- Upward pass: For : .
Proof. By Lemma 1, any optimal path arriving at either enters directly from , or enters from some and then moves purely vertically to row . If , the path moves downward from row to row ; the downward pass captures this by propagating costs from lower-indexed rows. If , the path moves upward; the upward pass captures this. Since both directions are covered and vertical reversals are suboptimal, the two passes suffice.
More formally, after the downward pass, equals the minimum cost among all paths that either enter from the left or enter some with from the left and travel down. After the upward pass, is further minimized over paths entering from below (i.e., from with traveling up). Together, all cases are covered.
Theorem 2. The answer is .
Proof. The problem requires the minimum over all valid paths. Since any valid path ends at some cell in column , the global minimum is the minimum over all ending rows.
Editorial
The key observation is that when we process one column at a time, every valid path enters the new column from the left exactly once and then moves vertically within that column. Because an optimal path never reverses vertical direction inside the same column, the only possibilities are a downward sweep or an upward sweep after entering.
That is why two passes are enough. We first assume every row enters directly from the left, then propagate improvements downward to capture paths that enter higher and move down, and finally propagate improvements upward to capture paths that enter lower and move up. The candidates for each row in the current column are exactly these three forms, so after the two sweeps the column is fully optimized.
Pseudocode
Initialize the current cost for each row using the values in the leftmost column.
For each later column:
Start a new column cost array by entering every row directly from the left
Sweep from top to bottom:
update each row using the possibility of coming from the row above
Sweep from bottom to top:
update each row using the possibility of coming from the row below
Replace the current cost array with this optimized column
Return the smallest value in the final column.
Complexity Analysis
Time: . There are columns, and for each column the downward and upward passes each take time, giving per column and total.
Space: . Only two arrays of length are needed: the current column’s DP values and the base array.
Answer
Code
Each problem page includes the exact C++ and Python source files from the local archive.
#include <bits/stdc++.h>
using namespace std;
int main(){
// Problem 82: Path Sum Three Ways
// Minimal path sum from left column to right column,
// moving right, up, or down.
int n = 80;
vector<vector<long long>> M(n, vector<long long>(n));
ifstream fin("p082_matrix.txt");
if(fin.is_open()){
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
fin >> M[i][j];
char ch;
if(j < n-1) fin >> ch;
}
}
fin.close();
} else {
n = 5;
M = {
{131, 673, 234, 103, 18},
{201, 96, 342, 965, 150},
{630, 803, 746, 422, 111},
{537, 699, 497, 121, 956},
{805, 732, 524, 37, 331}
};
// Expected for 5x5: 994
}
// DP column by column
vector<long long> dp(n);
for(int i = 0; i < n; i++)
dp[i] = M[i][0];
for(int j = 1; j < n; j++){
vector<long long> ndp(n);
// Start with cost of coming from the left
for(int i = 0; i < n; i++)
ndp[i] = dp[i] + M[i][j];
// Downward pass
for(int i = 1; i < n; i++)
ndp[i] = min(ndp[i], ndp[i-1] + M[i][j]);
// Upward pass
for(int i = n-2; i >= 0; i--)
ndp[i] = min(ndp[i], ndp[i+1] + M[i][j]);
dp = ndp;
}
cout << *min_element(dp.begin(), dp.end()) << endl;
// Full 80x80 answer: 260324
return 0;
}
"""
Problem 82: Path Sum: Three Ways
Find the minimal path sum from the left column to the right column,
moving right, up, or down in an 80x80 matrix.
Answer: 260324
"""
import os
def solve(matrix):
"""DP column-by-column with up/down propagation."""
n = len(matrix)
dp = [matrix[i][0] for i in range(n)]
for j in range(1, n):
# Cost coming from the left
ndp = [dp[i] + matrix[i][j] for i in range(n)]
# Downward pass
for i in range(1, n):
ndp[i] = min(ndp[i], ndp[i-1] + matrix[i][j])
# Upward pass
for i in range(n-2, -1, -1):
ndp[i] = min(ndp[i], ndp[i+1] + matrix[i][j])
dp = ndp
return min(dp)
def read_matrix(filename):
matrix = []
with open(filename) as f:
for line in f:
row = list(map(int, line.strip().split(',')))
matrix.append(row)
return matrix
def main():
script_dir = os.path.dirname(os.path.abspath(__file__))
filepath = os.path.join(script_dir, "p082_matrix.txt")
if os.path.exists(filepath):
matrix = read_matrix(filepath)
else:
matrix = [
[131, 673, 234, 103, 18],
[201, 96, 342, 965, 150],
[630, 803, 746, 422, 111],
[537, 699, 497, 121, 956],
[805, 732, 524, 37, 331],
]
# Expected: 994
answer = solve(matrix)
print(answer)
# Full 80x80 answer: 260324