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 D2018 · MediumGrid dynamic programming

Maximum-Score Ant Solitaire Path

National Olympiad — Regional round (2018 season)

Submission name: solitaire.cpp

Problem summary

Move from bottom-left to top-right in a card grid using only up and right. Maximize collected rank values, including both endpoints.

Input

N, then N rows of N rank-suit cards. Ranks:A,2…9,T,J,Q,K; suits:C,D,H,S.

Output

The maximum path score.

Constraints

1 ≤ N ≤100.

Worked example

Input and output for the worked example discussed below.

Input

4
8S AD 3C AC
8C 4H QD QS
5D 9H KC 7H
TC QC AS 2D

Output

69

Solution analysis

  1. Ignore suits. Convert A to1,T to10,J to11,Q to12,K to13; numeric ranks retain their values.
  2. A path reaching cell(r,c) comes from below or left. Set dp[r][c]=value[r][c]+max(dp[r+1][c],dp[r][c−1]), processing rows bottom-up and columns left-to-right.
  3. The sample path TC,QC,9H,KC,QD,QS,AC scores10+12+9+13+12+12+1=69. Positive values make zero-initialized borders safe: a valid predecessor’s score beats an absent predecessor except at the start.

Why it works

Every allowed path uses one of the two enumerated predecessors. Choosing the better optimal predecessor plus the fixed cell value therefore gives the optimum at that cell. The processing order satisfies dependencies, proving the final optimum by induction.

Complexity: O(N²) time and O(N²) space; scores are at most13(2N−1), within int.

Watch out. Start bottom-left, include both endpoint cards, and map face ranks correctly.

Commented C++17 solution

An independently written complete program with standard headers.

#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int rankValue(char rank){
    if(rank>='2'&&rank<='9'){
        return rank-'0';
    }
    const string ranks="ATJQK";
    const int points[5]={1,10,11,12,13};
    return points[ranks.find(rank)];
}
int main(){
    int n;
    cin>>n;
    vector<vector<int>>value(n+2,vector<int>(n+2));
    vector<vector<int>>dp(n+2,vector<int>(n+2));
    for(int row=1;row<=n;row++){
        for(int column=1;column<=n;column++){
            string card;
            cin>>card;
            value[row][column]=rankValue(card[0]);
        }
    }
    for(int row=n;row>=1;row--){
        for(int column=1;column<=n;column++){
            // Both legal predecessor states have already been processed.
            dp[row][column]=value[row][column]+max(dp[row+1][column],dp[row][column-1]);
        }
    }
    cout<<dp[1][n]<<'\n';
}

Source: Arena Olimpiici ↗