3332. Maximum Points Tourist Can Earn
Problem Description
You have a tourist visiting a country with n
cities, where every city is directly connected to every other city. The tourist will spend exactly k
days (indexed from 0 to k-1) traveling and can start their journey from any city.
On each day i
, the tourist has two options:
- Stay in the current city: If they remain in city
curr
on dayi
, they earnstayScore[i][curr]
points - Travel to a different city: If they move from city
curr
to citydest
, they earntravelScore[curr][dest]
points
The goal is to find the maximum total points the tourist can earn over the entire k
-day journey.
The solution uses dynamic programming where f[i][j]
represents the maximum points that can be earned by day i
when ending at city j
. The state transition considers both staying in the same city (when h == j
) and traveling from a different city h
to city j
. The answer is the maximum value among all cities on day k
.
The key insight is that for each day and each possible destination city, we need to consider all possible previous cities the tourist could have been in, choosing the option that maximizes the total score.
Intuition
The problem asks us to find the optimal sequence of decisions over k
days to maximize points. This is a classic dynamic programming scenario because:
-
Optimal substructure: The best solution for
k
days depends on the best solutions fork-1
days. If we know the maximum points we can achieve by dayi-1
ending at each city, we can determine the maximum points for dayi
. -
Overlapping subproblems: Multiple paths can lead to the same city on the same day, and we only care about the one with maximum points.
The key insight is to track the state as "what's the maximum score I can achieve by day i
if I end up in city j
?" This naturally leads to the DP formulation f[i][j]
= maximum points achievable by day i
ending at city j
.
For each new day i
and each possible destination city j
, we need to consider all possibilities:
- We could have been in city
j
already (stayed) and earnedstayScore[i-1][j]
- We could have been in any other city
h
and traveled toj
, earningtravelScore[h][j]
Since we want to maximize points, we try all possible previous cities h
and take the maximum. The recurrence becomes:
f[i][j] = max(f[i-1][h] + score)
where score
is either stayScore[i-1][j]
if h == j
or travelScore[h][j]
if h != j
.
Starting with f[0][j] = 0
for all cities (day 0, no points earned yet), we build up the solution day by day. The final answer is the maximum value among all cities on day k
, since the tourist can end their journey in any city.
Learn more about Dynamic Programming patterns.
Solution Approach
The solution implements a bottom-up dynamic programming approach using a 2D table to track the maximum scores.
Data Structure:
f[i][j]
: A 2D array wheref[i][j]
represents the maximum points achievable by dayi
when ending at cityj
- Initialize all values to negative infinity (
-inf
) except for day 0, which is set to 0 for all cities
Implementation Steps:
-
Initialization: Create a
(k+1) × n
DP table where:f[0][j] = 0
for all citiesj
(starting condition - no points earned on day 0)- All other entries are
-inf
to ensure they get updated with valid scores
-
State Transition: For each day
i
from 1 tok
:- For each possible destination city
j
:- For each possible previous city
h
:- Calculate the score based on whether the tourist stayed or traveled:
- If
j == h
(stayed): addstayScore[i-1][j]
tof[i-1][h]
- If
j != h
(traveled): addtravelScore[h][j]
tof[i-1][h]
- If
- Update
f[i][j]
with the maximum value found
- Calculate the score based on whether the tourist stayed or traveled:
- For each possible previous city
- For each possible destination city
-
Final Answer: Return
max(f[k])
- the maximum score among all possible ending cities on dayk
Time Complexity: O(k × n²)
- we iterate through k
days, and for each day, we consider all n²
city pairs (from city h
to city j
)
Space Complexity: O(k × n)
for the DP table
The algorithm systematically builds up the solution by considering all possible paths through the cities over k
days, ensuring we find the globally optimal solution.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example with n = 3
cities and k = 2
days.
Given:
stayScore = [[1, 3, 2], [2, 1, 4]]
(day 0 and day 1 stay scores)travelScore = [[0, 2, 1], [3, 0, 2], [1, 4, 0]]
(travel scores between cities)
Step 1: Initialize DP Table
Create f[3][3]
table (k+1 = 3 rows, n = 3 columns):
Day 0: f[0] = [0, 0, 0] // Starting condition Day 1: f[1] = [-∞, -∞, -∞] Day 2: f[2] = [-∞, -∞, -∞]
Step 2: Calculate Day 1
For each destination city j
, consider all possible previous cities h
:
For city 0 (j=0):
- From city 0 (h=0): stayed → f[0][0] + stayScore[0][0] = 0 + 1 = 1
- From city 1 (h=1): traveled → f[0][1] + travelScore[1][0] = 0 + 3 = 3
- From city 2 (h=2): traveled → f[0][2] + travelScore[2][0] = 0 + 1 = 1
- f[1][0] = max(1, 3, 1) = 3
For city 1 (j=1):
- From city 0 (h=0): traveled → f[0][0] + travelScore[0][1] = 0 + 2 = 2
- From city 1 (h=1): stayed → f[0][1] + stayScore[0][1] = 0 + 3 = 3
- From city 2 (h=2): traveled → f[0][2] + travelScore[2][1] = 0 + 4 = 4
- f[1][1] = max(2, 3, 4) = 4
For city 2 (j=2):
- From city 0 (h=0): traveled → f[0][0] + travelScore[0][2] = 0 + 1 = 1
- From city 1 (h=1): traveled → f[0][1] + travelScore[1][2] = 0 + 2 = 2
- From city 2 (h=2): stayed → f[0][2] + stayScore[0][2] = 0 + 2 = 2
- f[1][2] = max(1, 2, 2) = 2
After Day 1: f[1] = [3, 4, 2]
Step 3: Calculate Day 2 For city 0 (j=0):
- From city 0: stayed → f[1][0] + stayScore[1][0] = 3 + 2 = 5
- From city 1: traveled → f[1][1] + travelScore[1][0] = 4 + 3 = 7
- From city 2: traveled → f[1][2] + travelScore[2][0] = 2 + 1 = 3
- f[2][0] = max(5, 7, 3) = 7
For city 1 (j=1):
- From city 0: traveled → f[1][0] + travelScore[0][1] = 3 + 2 = 5
- From city 1: stayed → f[1][1] + stayScore[1][1] = 4 + 1 = 5
- From city 2: traveled → f[1][2] + travelScore[2][1] = 2 + 4 = 6
- f[2][1] = max(5, 5, 6) = 6
For city 2 (j=2):
- From city 0: traveled → f[1][0] + travelScore[0][2] = 3 + 1 = 4
- From city 1: traveled → f[1][1] + travelScore[1][2] = 4 + 2 = 6
- From city 2: stayed → f[1][2] + stayScore[1][2] = 2 + 4 = 6
- f[2][2] = max(4, 6, 6) = 6
After Day 2: f[2] = [7, 6, 6]
Step 4: Find Maximum
The answer is max(f[2]) = max(7, 6, 6) = 7
The optimal path: Start in city 1 → Stay in city 1 on day 0 (earn 3) → Travel to city 0 on day 1 (earn 4) → Total: 7 points.
Solution Implementation
1from typing import List
2
3class Solution:
4 def maxScore(
5 self, n: int, k: int, stayScore: List[List[int]], travelScore: List[List[int]]
6 ) -> int:
7 # Initialize DP table
8 # dp[day][city] = maximum score achievable by day 'day' ending at city 'city'
9 dp = [[float('-inf')] * n for _ in range(k + 1)]
10
11 # Base case: Day 0, can start at any city with score 0
12 dp[0] = [0] * n
13
14 # Fill the DP table for each day
15 for day in range(1, k + 1):
16 # Consider ending at each city on current day
17 for current_city in range(n):
18 # Consider all possible previous cities
19 for previous_city in range(n):
20 # Calculate score for this transition
21 if current_city == previous_city:
22 # Stayed in the same city
23 score = dp[day - 1][previous_city] + stayScore[day - 1][current_city]
24 else:
25 # Traveled from previous_city to current_city
26 score = dp[day - 1][previous_city] + travelScore[previous_city][current_city]
27
28 # Update maximum score for current day and city
29 dp[day][current_city] = max(dp[day][current_city], score)
30
31 # Return the maximum score across all cities on the last day
32 return max(dp[k])
33
1class Solution {
2 public int maxScore(int n, int k, int[][] stayScore, int[][] travelScore) {
3 // dp[day][city] = maximum score achievable by day 'day' ending at city 'city'
4 int[][] dp = new int[k + 1][n];
5
6 // Initialize all values to negative infinity (impossible state)
7 for (int[] row : dp) {
8 Arrays.fill(row, Integer.MIN_VALUE);
9 }
10
11 // Base case: on day 0, we can start at any city with score 0
12 Arrays.fill(dp[0], 0);
13
14 // Fill the DP table for each day
15 for (int day = 1; day <= k; day++) {
16 // Consider ending at each city on current day
17 for (int currentCity = 0; currentCity < n; currentCity++) {
18 // Consider all possible previous cities from previous day
19 for (int previousCity = 0; previousCity < n; previousCity++) {
20 // Calculate score based on whether we stayed or traveled
21 int score;
22 if (currentCity == previousCity) {
23 // Stayed in the same city
24 score = dp[day - 1][previousCity] + stayScore[day - 1][currentCity];
25 } else {
26 // Traveled from previousCity to currentCity
27 score = dp[day - 1][previousCity] + travelScore[previousCity][currentCity];
28 }
29
30 // Update maximum score for current day and city
31 dp[day][currentCity] = Math.max(dp[day][currentCity], score);
32 }
33 }
34 }
35
36 // Find the maximum score across all cities on the last day
37 return Arrays.stream(dp[k]).max().getAsInt();
38 }
39}
40
1class Solution {
2public:
3 int maxScore(int n, int k, vector<vector<int>>& stayScore, vector<vector<int>>& travelScore) {
4 // dp[day][city] = maximum score achievable by day 'day' ending at city 'city'
5 // Initialize with negative infinity (0xc0c0c0c0 in hex represents a large negative number)
6 int dp[k + 1][n];
7 memset(dp, 0xc0, sizeof(dp));
8
9 // Base case: day 0, can start at any city with score 0
10 memset(dp[0], 0, sizeof(dp[0]));
11
12 // Iterate through each day
13 for (int day = 1; day <= k; ++day) {
14 // For each destination city on current day
15 for (int destCity = 0; destCity < n; ++destCity) {
16 // Try all possible source cities from previous day
17 for (int srcCity = 0; srcCity < n; ++srcCity) {
18 // Calculate score based on whether we stay or travel
19 int score;
20 if (destCity == srcCity) {
21 // Stay in the same city
22 score = dp[day - 1][srcCity] + stayScore[day - 1][destCity];
23 } else {
24 // Travel from srcCity to destCity
25 score = dp[day - 1][srcCity] + travelScore[srcCity][destCity];
26 }
27
28 // Update maximum score for current state
29 dp[day][destCity] = max(dp[day][destCity], score);
30 }
31 }
32 }
33
34 // Return the maximum score among all cities on the last day
35 return *max_element(dp[k], dp[k] + n);
36 }
37};
38
1/**
2 * Calculates the maximum score achievable by visiting cities over k days
3 * @param n - Number of cities
4 * @param k - Number of days
5 * @param stayScore - 2D array where stayScore[day][city] is the score for staying in a city
6 * @param travelScore - 2D array where travelScore[from][to] is the score for traveling between cities
7 * @returns The maximum score achievable
8 */
9function maxScore(n: number, k: number, stayScore: number[][], travelScore: number[][]): number {
10 // dp[day][city] represents the maximum score achievable by being in 'city' on 'day'
11 const dp: number[][] = Array.from({ length: k + 1 }, () => Array(n).fill(-Infinity));
12
13 // Initialize day 0: can start from any city with score 0
14 dp[0].fill(0);
15
16 // Iterate through each day
17 for (let day = 1; day <= k; ++day) {
18 // Consider ending in each city on current day
19 for (let currentCity = 0; currentCity < n; ++currentCity) {
20 // Try coming from each possible previous city
21 for (let previousCity = 0; previousCity < n; ++previousCity) {
22 // Calculate score based on whether we stayed or traveled
23 const score = currentCity === previousCity
24 ? stayScore[day - 1][currentCity] // Stayed in the same city
25 : travelScore[previousCity][currentCity]; // Traveled from previous to current city
26
27 // Update maximum score for being in currentCity on current day
28 dp[day][currentCity] = Math.max(
29 dp[day][currentCity],
30 dp[day - 1][previousCity] + score
31 );
32 }
33 }
34 }
35
36 // Return the maximum score among all cities on the last day
37 return Math.max(...dp[k]);
38}
39
Time and Space Complexity
Time Complexity: O(k * n^2)
The algorithm uses three nested loops:
- The outermost loop iterates
k
times (from 1 to k) - The middle loop iterates
n
times (for each city j) - The innermost loop iterates
n
times (for each previous city h)
For each iteration of these three loops, we perform constant time operations (max comparison and array lookups). Therefore, the total time complexity is O(k * n * n) = O(k * n^2)
.
Space Complexity: O(k * n)
The space complexity is determined by:
- The 2D DP table
f
with dimensions(k+1) × n
, which requiresO((k+1) * n) = O(k * n)
space - A few scalar variables (
i
,j
,h
) which requireO(1)
space
Therefore, the total space complexity is O(k * n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Index Mapping for Score Arrays
The most common mistake is misunderstanding how the day indices map to the score arrays. Since days are indexed from 0 to k-1, but the DP table uses indices from 0 to k (where day 0 is the initialization day with no actions), there's an off-by-one relationship:
- On day
i
in the DP table (where i ≥ 1), we're actually taking actions for dayi-1
in the problem - Therefore, we should use
stayScore[i-1]
andtravelScore
when computingdp[i]
Incorrect Implementation:
# Wrong: Using day index directly score = dp[day - 1][previous_city] + stayScore[day][current_city] # Index out of bounds!
Correct Implementation:
# Correct: Adjusting for 0-indexed score arrays score = dp[day - 1][previous_city] + stayScore[day - 1][current_city]
2. Space Optimization Pitfall
While it's tempting to optimize space by using only two 1D arrays (previous day and current day), be careful not to overwrite values that are still needed:
Problematic Space-Optimized Version:
# This can lead to bugs if not careful about copying
prev = [0] * n
curr = [float('-inf')] * n
for day in range(1, k + 1):
for current_city in range(n):
for previous_city in range(n):
# ... calculate score ...
curr[current_city] = max(curr[current_city], score)
prev = curr # Wrong! This creates a reference, not a copy
curr = [float('-inf')] * n # Need to reset for next iteration
Correct Space-Optimized Version:
prev = [0] * n
for day in range(1, k + 1):
curr = [float('-inf')] * n
for current_city in range(n):
for previous_city in range(n):
if current_city == previous_city:
score = prev[previous_city] + stayScore[day - 1][current_city]
else:
score = prev[previous_city] + travelScore[previous_city][current_city]
curr[current_city] = max(curr[current_city], score)
prev = curr # Now prev points to the completed curr array
3. Initialization Error
Forgetting to properly initialize the DP table can lead to incorrect results. The base case must allow the tourist to start from any city:
Wrong Initialization:
dp = [[0] * n for _ in range(k + 1)] # Wrong! Non-reachable states should be -inf
dp[0][0] = 0 # Wrong! Tourist can start from any city
Correct Initialization:
dp = [[float('-inf')] * n for _ in range(k + 1)]
dp[0] = [0] * n # Tourist can start from any city with 0 initial score
Which of the following uses divide and conquer strategy?
Recommended Readings
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!