CompSci.rocks
FRQcsapa

Trail: 2010 FRQ 3

A step-by-step solution to the 2010 AP CSA FRQ 3 (Trail), covering tracking a running maximum and minimum across an array segment and counting significant changes between consecutive elements in Java.

Reading elevation data out of a plain array of ints drives this AP Computer Science A free-response question, which asks for two unrelated methods on the same class — one that checks how "flat" a stretch of a hiking trail is, and another that counts how many times the trail suddenly rises or drops.

What This FRQ Tests

  • AP CSA units: Unit 6 (Array) and Unit 4 (Iteration)
  • Core skill: tracking a running maximum and minimum while scanning a subrange of an array
  • Secondary skill: comparing each array element to its neighbor to detect and count meaningful changes
  • Official category: "Array/ArrayList" — this coincidentally lands in the same slot a modern exam would use for array-based work, but 2010 predates the standardized FRQ-number-to-category order that AP CSA formalized starting with the 2019–2020 CED redesign, so that alignment isn't guaranteed for every older year. It's printed as FRQ 3 in the original exam.

The Setup

  • The given Trail class has:
    • private int[] markers — the elevation at each marker; the first marker is index 0, and markers.length is the total number of markers
  • You're asked to write two unrelated methods:
    • isLevelTrailSegment(int start, int end) — precondition 0 <= start < end <= markers.length - 1; returns true if the difference between the highest and lowest elevation in that segment is <= 10 meters
    • isDifficult() — counts how many consecutive-marker elevation changes are >= 30 meters in magnitude (up or down); returns true if there are 3 or more

Part (a): Writing isLevelTrailSegment(int start, int end)

The Rule, Broken Down

  1. A "trail segment" spans every marker from start to end, inclusive of both endpoints.
  2. Find the highest and lowest elevation anywhere in that range.
  3. The segment is level if highest - lowest is 10 or less — note this is inclusive, so a difference of exactly 10 still counts as level.

Step-by-Step Approach

  1. Initialize both a running max and min to markers[start] — the first marker in the segment is a safe starting point for both.
  2. Loop from start + 1 through end, inclusive.
  3. On each iteration, update max if this marker's elevation is higher, and update min if it's lower.
  4. After the loop, return whether max - min is <= 10.

The Code

public boolean isLevelTrailSegment(int start, int end)
{
    int max = markers[start];
    int min = markers[start];

    for (int i = start + 1; i <= end; i++)
    {
        if (markers[i] > max)
        {
            max = markers[i];
        }

        if (markers[i] < min)
        {
            min = markers[i];
        }
    }

    return max - min <= 10;
}

Why Each Piece Matters

  • Initializing max and min to markers[start], not to 0 or some arbitrary sentinel value — elevations aren't guaranteed to be positive or fall in any particular range, so starting from an actual data point already inside the segment is always safe.
  • The loop starts at start + 1, not start — the marker at start is already accounted for by the initialization, so re-checking it would just be redundant.
  • i <= end, not i < end — the segment includes the end marker itself, and the precondition guarantees end is always a valid index, so there's no out-of-bounds risk.
  • Two separate if statements, not if/else if — checking for a new max and a new min are independent questions; a single marker's elevation needs to be compared against both max and min every iteration.

Part (b): Writing isDifficult()

The Rule, Broken Down

  1. Compare every pair of consecutive markers: markers[i] and markers[i + 1].
  2. Compute the elevation change between them — it can be positive (uphill) or negative (downhill).
  3. A change counts as "significant" if its magnitude is 30 meters or more, regardless of direction.
  4. If there are 3 or more significant changes anywhere on the trail, it's rated difficult.

Step-by-Step Approach

  1. Start a counter at 0.
  2. Loop i from 0 up to (but not including) markers.length - 1 — this keeps i + 1 a valid index on every iteration.
  3. On each iteration, compute markers[i + 1] - markers[i].
  4. Take the absolute value of that change and compare it to 30; if it's at least 30, increment the counter.
  5. After the loop, return whether the counter is 3 or more.

The Code

public boolean isDifficult()
{
    int count = 0;

    for (int i = 0; i < markers.length - 1; i++)
    {
        int change = markers[i + 1] - markers[i];

        if (Math.abs(change) >= 30)
        {
            count++;
        }
    }

    return count >= 3;
}

Why Each Piece Matters

  • markers.length - 1 as the loop bound — since the loop body always looks ahead to markers[i + 1], letting i reach markers.length - 1 itself would push i + 1 one index past the end of the array.
  • Math.abs(change) — a "change" can be a downhill drop (a negative number), and the rule cares about the size of the change, not its direction. Math.abs is listed on the AP Quick Reference sheet, so it's the natural default here rather than writing a manual if to flip the sign.
  • Comparing against 30 with >=, not > — a change of exactly 30 meters still counts, per the rule's own wording ("at least 30 meters").
  • count >= 3, not count == 3 — "3 or more" means any count of 3 or higher should be rated difficult, not exactly 3.

Tracing the Example

Using the trail data from the question — elevations 100, 150, 105, 120, 90, 80, 50, 75, 75, 70, 80, 90, 100 at indices 012:

Markers Change Math.abs(change) >= 30?
0 → 1 150 − 100 = 50 50 yes
1 → 2 105 − 150 = −45 45 yes
2 → 3 120 − 105 = 15 15 no
3 → 4 90 − 120 = −30 30 yes
4 → 5 80 − 90 = −10 10 no
5 → 6 50 − 80 = −30 30 yes
6 → 7 75 − 50 = 25 25 no
7 → 8 75 − 75 = 0 0 no
8 → 9 70 − 75 = −5 5 no
9 → 10 80 − 70 = 10 10 no
10 → 11 90 − 80 = 10 10 no
11 → 12 100 − 90 = 10 10 no

That's 4 significant changes (between markers 0–1, 1–2, 3–4, and 5–6) — exactly matching the question's own count and its list of which marker pairs triggered it. Since 4 >= 3, isDifficult() returns true, and this trail is rated difficult, also matching the question.

Common Mistakes to Avoid

  • Comparing change >= 30 without Math.abs. This misses every downhill drop entirely — the -45 and -30 changes above would never be counted, undercounting the true number of significant changes.
  • Looping to markers.length instead of markers.length - 1. This throws an ArrayIndexOutOfBoundsException on the final iteration, when markers[i + 1] reaches past the last valid index.
  • Using > 30 instead of >= 30. Two of the four significant changes in the example above are exactly 30 in magnitude and must still be counted.
  • Returning count == 3 instead of count >= 3. A trail with 4, 5, or more significant changes is still difficult, not just one with exactly 3.

Notes: Tracking Max/Min With Math.max/Math.min

Part (a)'s running max/min can be written more compactly using two more Math methods:

public boolean isLevelTrailSegment(int start, int end)
{
    int max = markers[start];
    int min = markers[start];

    for (int i = start + 1; i <= end; i++)
    {
        max = Math.max(max, markers[i]);
        min = Math.min(min, markers[i]);
    }

    return max - min <= 10;
}
  • Math.max and Math.min aren't listed on the AP Quick Reference sheet — only Math.abs, Math.pow, Math.sqrt, and Math.random are. That doesn't make them off-limits; AP CSA graders accept any correct Java. The tradeoff is simply that you won't have their exact signatures printed for you to double-check mid-exam, the way you would with the if-based version above.
  • Each line replaces a two-line if block with a single reassignment, which is a common shorthand worth recognizing even if you choose to write the longer version on the actual exam.

Key Takeaways

  • Tracking a running maximum and minimum over part of an array is a two-variable pattern: initialize both from the first element in range, then update each independently as you scan the rest.
  • When checking whether a numeric change is "significant" in either direction, take its absolute value before comparing it to a threshold — comparing the raw signed value only ever catches increases.
  • A loop that looks ahead to i + 1 (or back to i - 1) needs an adjusted bound (length - 1, not length) so the lookahead index never steps outside the array.

Related FRQs