Fundamentals 12 min read

How Heap and Recursion Can Select the Best Tourist Attractions

The article explains how to use a max‑heap together with backtracking and a greedy strategy in Python to recommend the top three attractions with the highest scores while keeping the total travel distance under 20 km, and discusses the algorithm's steps, example execution, and limitations.

YiSu Grain
YiSu Grain
YiSu Grain
How Heap and Recursion Can Select the Best Tourist Attractions

The problem is to recommend the three highest‑rated tourist attractions from a list, ensuring that the sum of their straight‑line distances from the visitor’s current location does not exceed 20 km. Each attraction is represented by a name, a rating (1–10), and a distance in kilometers.

Algorithm Design

The solution combines three techniques:

Use a priority queue (implemented as a max‑heap) to always consider the highest‑scoring attractions first.

Apply a backtracking search to explore different combinations and enforce the distance constraint.

Employ a greedy rule that stops the search as soon as a valid combination of three attractions is found.

Python Implementation

import heapq

def recommend_attractions(attractions, max_distance=20, num_recommendations=3):
    # Build a list of tuples (negative_score, name, distance) for max‑heap behavior
    pq = [(-score, name, distance) for name, score, distance in attractions]
    heapq.heapify(pq)

    def backtrack(index, current_distance, path):
        if len(path) == num_recommendations:
            return path
        for i in range(index, len(pq)):
            score, name, distance = pq[i]
            if current_distance + distance <= max_distance:
                result = backtrack(i + 1, current_distance + distance,
                                   path + [(name, -score, distance)])
                if result:
                    return result
        return None

    return backtrack(0, 0, [])

# Example data
attractions = [
    ('Attraction A', 9, 5),
    ('Attraction B', 8, 8),
    ('Attraction C', 7, 3),
    ('Attraction D', 10, 13),
    ('Attraction E', 6, 2),
    ('Attraction F', 8, 4),
]

result = recommend_attractions(attractions)
if result:
    print("Recommended attractions:")
    for name, score, distance in result:
        print(f"{name}: rating {score}, distance {distance}km")
else:
    print("No suitable attractions found")

Explanation of Key Steps

The list comprehension creates tuples with the rating negated so that heapq (a min‑heap) behaves as a max‑heap. heapq.heapify(pq) transforms the list into a heap, placing the highest rating at the top.

The inner backtrack function receives the current index in the heap, the accumulated distance, and the current path of selected attractions.

If the path already contains three attractions, it is returned as a valid solution.

The for loop iterates from the current index to the end of the heap, ensuring each attraction is considered at most once.

When adding an attraction would keep the total distance within the limit, the function recurses with the updated distance and path.

When a recursive call finds a valid combination, return result propagates the solution up the call stack, terminating further exploration.

Step‑by‑Step Example

Using a smaller input:

attractions = [
    ('A', 9, 5),
    ('B', 8, 8),
    ('C', 7, 3),
    ('D', 10, 7),
]
max_distance = 20
num_recommendations = 3

Execution proceeds as follows:

Heap creation yields [(-10, 'D', 7), (-9, 'A', 5), (-8, 'B', 8), (-7, 'C', 3)] and heapq.heapify arranges it as a heap. backtrack(0, 0, []) starts the search.

First level picks D (distance 7), recurses to backtrack(1, 7, [('D', 10, 7)]).

Second level picks A (distance 5), recurses to backtrack(2, 12, [('D', 10, 7), ('A', 9, 5)]).

Third level picks B (distance 8), reaching total distance 20, which satisfies the limit, so the path [('D', 10, 7), ('A', 9, 5), ('B', 8, 8)] is returned.

The result propagates back through each recursion level and is finally returned by recommend_attractions.

Limitations and Performance Considerations

If a chosen attraction would exceed the distance limit, that branch returns None and the algorithm tries the next candidate.

The current implementation traverses the heap by index order, so it does not fully exploit the heap’s ability to pop the maximum element efficiently.

The algorithm stops after finding the first feasible combination, which may not be the optimal solution in terms of total rating or distance utilization.

Finding the true optimal set would require exploring all combinations, leading to exponential time complexity for large datasets; more advanced heuristics or dynamic programming would be needed in that case.

Overall, the approach demonstrates how a max‑heap can prioritize high‑score items while a backtracking search enforces a global constraint, providing a clear, educational example of combining data structures and algorithmic techniques.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Pythonrecommendation systemData Structuresbacktrackingheapgreedy algorithm
YiSu Grain
Written by

YiSu Grain

A fleeting mayfly in the world, a single grain in the boundless sea.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.