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 D2016 · MediumDynamic programming

Minimum-Score Slalom

National Olympiad — National round (2016 season)

Submission name: slalom.cpp

Problem summary

Pass gates in index order, optionally skipping any gate. Each visited gate and the finish add one point; travelled straight-line distances and skipped-gate penalties also add to the score. Find the smallest total.

Source clarification. One original score sentence prints a minus before penalties, but the stated penalty rule and official sample require adding them. This implementation follows that intended nonnegative penalty convention.

Input

Start and finish coordinates, M, then M rows x y penalty.

Output

The optimum score to three digits after the decimal point.

Constraints

0 ≤ M ≤ 25; coordinates and penalties are integers from 0 to 1000.

Worked example

Input and output for the worked example discussed below.

Input

0 0 100 100
3
30 30 90
60 60 80
10 90 10

Output

154.421

Solution analysis

  1. Treat the start as vertex 0 and the finish as M+1. If the previous visited vertex is i and the next is j>i, the transition adds their Euclidean distance, one visit point, and all penalties of gates strictly between them.
  2. Use prefix sums of penalties to obtain skipped penalties in O(1). Then dp[j] is the minimum over every earlier i of dp[i]+distance(i,j)+1+skippedPenalty(i,j), with dp[0]=0. This is a shortest path in an index-ordered DAG.
  3. In the sample, visit gates 1 and 2 and skip gate 3. Start, gate 1, gate 2 and finish lie on the main diagonal, so distance is sqrt(20000); add three visit points and penalty 10, yielding 154.421.

Why it works

The last visited predecessor partitions every valid route uniquely into an optimal earlier route and one transition containing exactly its skipped penalties.

Complexity: O(M²) time for all predecessor pairs and O(M) space for coordinates, prefix sums and DP.

Watch out. The start earns no visit point, while the finish does. Penalties are added; the original text contains a contradictory minus sign in one sentence, but its penalty rule and sample require addition.

Commented C++17 solution

An independently written complete program with standard headers.

// The last visited predecessor partitions every valid route uniquely into an optimal earlier route and one
// transition containing exactly its skipped penalties..
#include<cmath>
#include<iomanip>
#include<iostream>
#include<limits>
#include<vector>
using namespace std;
int main(){
    int sx,sy,fx,fy;
    cin>>sx>>sy>>fx>>fy;
    int gates;
    cin>>gates;
    vector<int>x(gates+2),y(gates+2),penalty(gates+2),prefix(gates+2);
    x[0]=sx;
    y[0]=sy;
    x[gates+1]=fx;
    y[gates+1]=fy;
    for(int gate=1;gate<=gates;gate++){
        cin>>x[gate]>>y[gate]>>penalty[gate];
        prefix[gate]=prefix[gate-1]+penalty[gate];
    }
    vector<double>best(gates+2,numeric_limits<double>::infinity());
    best[0]=0;
    for(int next=1;next<=gates+1;next++){
        for(int previous=0;previous<next;previous++){
            double distance=hypot(x[next]-x[previous],y[next]-y[previous]);
            best[next]=min(best[next],best[previous]+distance+1+prefix[next-1]-prefix[previous]);
        }
    }
    cout<<fixed<<setprecision(3)<<best.back()<<'\n';
}

Source: Arena Olimpiici ↗