cppAcademy
FROM LEARNING TO COMPETING

Intermediate.
Put your skills to the test.

Bulgarian Informatics Group D · 2007–2026

270 independently explained problems: 54 Municipal, 54 Regional, 105 National Olympiad and 57 Autumn Tournament tasks. Start with the easier tasks and work upwards. Difficulty labels are learning estimates, not official ratings; Group D also includes challenging national-level problems. The six 2017 National tasks shared by Groups C and D use their existing permanent URLs. Years follow Arena’s competition-season labels. Source clarifications and special grader requirements are noted on affected tasks.
GROUP D2015 · MediumGrid dynamic programming

Minimum-Weight Path Across Matrix Columns

National Olympiad — National round (2015 season)

Submission name: table.cpp

Problem summary

Start anywhere in the leftmost column. Move to the next column in the same or an adjacent row. Find the minimum visited-cell sum on reaching the rightmost column.

Input

m n followed by m rows of n integers.

Output

The minimum path sum.

Constraints

1 < m,n < 1000; entries are positive integers below 100.

Worked example

Input and output for the worked example discussed below.

Input

4 6
3 4 1 2 8 6
6 1 8 2 7 4
5 9 3 9 9 5
8 4 1 3 2 6

Output

17

Solution analysis

  1. Let previous[r] be the cheapest cost reaching row r in the previous column. Initialize it with each first-column value, because the starting row is unrestricted.
  2. For each new column, current[r] is its value plus the minimum of previous[r] and any valid previous neighbours r−1,r+1. Compute a fresh array so transitions never reuse the current column.
  3. In the sample, column 2 costs become [7,4,14,9]. Continue the same recurrence, then take the minimum over final rows; the full answer is 17. There is no first-row/last-row wrapping.

Why it works

Each legal path to a cell comes from exactly one of its three permitted predecessors. Taking the smallest optimal predecessor cost and adding the cell value gives the optimal cost by induction on columns. Minimizing the final row handles all possible endpoints.

Complexity: O(mn) time and O(mn) input storage, plus O(m) DP working space.

Watch out. Do not update the previous column in place. Starting and ending rows are free choices; row moves are at most one and never wrap.

Commented C++17 solution

An independently written complete program with standard headers.

#include<algorithm>
#include<iostream>
#include<limits>
#include<vector>
using namespace std;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int rows;
    int columns;
    cin>>rows>>columns;
    vector<vector<int>>grid(rows,vector<int>(columns));
    for(auto&row:grid){
        for(int&value:row){
            cin>>value;
        }
    }
    vector<int>previous(rows);
    for(int r=0;r<rows;r++){
        previous[r]=grid[r][0];
    }
    for(int c=1;c<columns;c++){
        vector<int>current(rows,numeric_limits<int>::max());
        for(int r=0;r<rows;r++){
            // Read all predecessors from the previous column only.
            for(int offset=-1;offset<=1;offset++){
                int before=r+offset;
                if(before>=0&&before<rows){
                    current[r]=min(current[r],previous[before]+grid[r][c]);
                }
            }
        }
        previous=current;
    }
    cout<<*min_element(previous.begin(),previous.end())<<'\n';
}

Source: Arena Olimpiici ↗