ICPC 2023
ICPC 2023

J. Bridging the Gap

47th Annual hosted by ICPC World Championship AASTMT Problem J Bridging the Gap Time limit...

Updated May 21, 2026
Track ICPC
Year 2023
Statement Text + PDF
TeXC++Statement textStatement PDF

Problem Statement

Formatted from the contest statement text, with sample tests broken out into copyable blocks.

  47th Annual                                                                                    hosted by
  ICPC World
Championship                                                                                     AASTMT
                                              Problem J
                                         Bridging the Gap
                                       Time limit: 4 seconds
A group of walkers arrives at a river in the night. They
want to cross a bridge, which can hold a limited number
of walkers at a time. The walkers have just one torch,
which needs to be used when crossing the bridge. Each
walker takes a certain time to cross; a group crossing
together must walk at the slowest walker’s pace. What
is the shortest time it takes for all walkers to cross the
bridge?
For example, Sample Input 1 assumes the bridge can
hold 2 walkers at a time and there are 4 walkers with
crossing times 1 minute, 2 minutes, 5 minutes and 10
                                                                                         A bridge with low capacity
minutes, respectively. The shortest time of 17 minutes
can be achieved by the following sequence of crossings.
First, the two fastest walkers cross in 2 minutes. Second, the fastest walker crosses back in 1 minute.
Third, the two slowest walkers cross in 10 minutes. Fourth, the second-fastest walker crosses back in 2
minutes. Fifth, the two fastest walkers cross in 2 minutes.

Input

The first line of input contains two integers n and c, where n (2 ≤ n ≤ 104 ) is the number of walkers, and c (2 ≤ c ≤ 104 ) is the number of walkers the bridge can hold at a time. Then follows a line containing n integers t1 , . . . , tn (1 ≤ ti ≤ 109 for all i). The ith walker takes time ti to cross.

Output

Output the minimum total time it takes for the entire group to cross the bridge.

Sample Tests

Sample 1
Sample Input
 4 2
 1 2 10 5
Sample Output
17
Sample 2
Sample Input
 4 6
 1 2 10 5
Sample Output
10

Editorial

The solution write-up is rendered from the LaTeX source, with equations kept live through MathJax.

Key Observations

  • Sort the walkers by time: \[ t_1 \le t_2 \le \cdots \le t_n. \] In an optimal solution, every backward crossing is made by exactly one walker: if several walkers came back together, keeping only the fastest of them is never worse.

  • Before the last trip, a forward crossing consists of some number of currently slowest walkers that are meant to stay on the far side, plus some number of fastest walkers that will later return one by one. Using any non-fast walker as a future returner is dominated by using a faster one.

  • This suggests a dynamic program over:

    • how many slow walkers have already been moved permanently;

    • how many future one-person return trips have already been ``paid for'' by sending fast helpers across earlier.

    • Besides mixed trips that move slow walkers, we may also take a fast-only package: send $h+1$ fastest walkers across, planning for $h$ of them to return later. This produces $h$ future return credits and costs \[ t_{h+1} + \sum_{i=1}^{h} t_i. \] These package costs can be precomputed with an unbounded knapsack.

    Algorithm

    1. Sort the crossing times increasingly.

    2. Precompute credit_cost[x] = minimum extra cost of fast-only packages that create exactly $x$ future return credits.

    3. Let dp[done][credit] be the minimum cost after the done slowest walkers have been moved permanently, with credit net prepaid returns available.

    4. From state (done, credit), choose how many fast helpers accompany the next block of slow walkers. If helpers = h, then:

      • the crossing moves $c-h$ of the remaining slow walkers permanently;

      • its time contribution is the slowest moved walker plus $\sum_{i=1}^{h} t_i$, because those $h$ fast helpers will later return individually;

      • the net number of credits changes by $h-1$, since every non-final forward crossing needs one return trip.

      • The DP only keeps the credit range that can still matter, which reduces the number of reachable states to $O(n^2/c)$.

      • After all slow walkers have been transferred, add enough fast-only packages to settle any remaining credit deficit and take the best final cost. enumerate

        Correctness Proof

        We prove that the algorithm returns the correct answer.

        Lemma 1.

        There exists an optimal solution in which every backward crossing consists of a single walker.

        Proof.

        Suppose some backward crossing contains several walkers. Replacing it by only the fastest walker from that group still returns the torch to the near side and cannot increase the time of that crossing. The other walkers can simply remain on the far side. Repeating this transformation yields an optimal solution with only single-person returns. □

        Lemma 2.

        There exists an optimal solution in which every forward crossing is either:

        • a group of the currently slowest remaining walkers, possibly together with some of the globally fastest walkers that will later return; or

        • a fast-only package.

        Proof.

        By Lemma 1, future returners always cross back alone. If a forward trip contains a walker who will later return, replacing that walker by a faster not-yet-used walker can only decrease future return costs and cannot increase the forward trip time. Thus temporary helpers may be assumed to be chosen from the globally fastest walkers.

        The walkers intended to stay permanently on the far side may then be taken in decreasing order of speed: if two permanent walkers are transferred in the opposite order, swapping them does not worsen any trip, because only the slowest walker in each forward group matters. Hence some optimal solution has exactly the stated structure. □

        Lemma 3.

        Every schedule of the form described in Lemma 2 is represented by a unique path in the dynamic program, and every DP path corresponds to a valid schedule of that form.

        Proof.

        The state records precisely the two quantities needed to continue optimally: how many slow walkers have already been transferred permanently, and how many prepaid one-person returns are still available. Each transition chooses the number of fast helpers on the next forward trip, which uniquely determines how many slow walkers are moved, what time is added, and how the credit balance changes. Conversely, any schedule of the structured form yields exactly those state changes. Therefore the DP and the structured schedules are in one-to-one correspondence. □

        Theorem.

        The algorithm outputs the minimum total crossing time.

        Proof.

        By Lemmas 1 and 2, some optimal solution has the structured form modeled by the DP. By Lemma 3, the DP explores exactly all such solutions and computes their exact total cost, including the cheapest possible fast-only packages needed to settle the final credit balance. Therefore the minimum value produced by the DP is the true optimum. □

        Complexity Analysis

        The pruned state space has size $O(n^2/c)$, and each state has $O(c)$ outgoing transitions, so the main DP runs in $O(n^2)$ time. The fast-only package preprocessing is also $O(n^2)$ in the worst case. The memory usage is $O(n^2/c)$.

        Implementation Notes

        • The code stores only the credit range that can still matter for a given done, which is why each DP row has a different width.

        • If $c \ge n$, everybody can cross together immediately and the answer is simply the slowest walker's time.

Code

C++ solution used for this page.

C++

Clean code view with a raw-file link when you want the original source.

Raw file
#include <bits/stdc++.h>
using namespace std;

namespace {

void solve() {
    int n, c;
    cin >> n >> c;

    vector<long long> t(n);
    for (int i = 0; i < n; ++i) {
        cin >> t[i];
    }
    sort(t.begin(), t.end());

    if (c >= n) {
        cout << t.back() << '\n';
        return;
    }

    const long long INF = (1LL << 62);

    vector<long long> fast_prefix(n + 1, 0);
    for (int i = 0; i < n; ++i) {
        fast_prefix[i + 1] = fast_prefix[i] + t[i];
    }

    // credit_cost[x]: minimum cost of "fast-only" trips that produce x future return credits.
    vector<long long> credit_cost(n, INF);
    credit_cost[0] = 0;
    for (int helpers = 1; helpers < c && helpers < n; ++helpers) {
        long long package_cost = t[helpers] + fast_prefix[helpers + 1];
        for (int have = helpers; have < n; ++have) {
            credit_cost[have] = min(credit_cost[have], credit_cost[have - helpers] + package_cost);
        }
    }

    vector<int> min_credit(n + 1), max_credit(n + 1);
    vector<vector<long long>> dp(n + 1);
    for (int done = 0; done <= n; ++done) {
        min_credit[done] = -(done / c) - (done == n ? 1 : 0);
        max_credit[done] = (n - done + c - 1) / c - 1;
        dp[done].assign(max_credit[done] - min_credit[done] + 1, INF);
    }
    dp[0][-min_credit[0]] = 0;

    for (int done = 0; done < n; ++done) {
        for (int idx = 0; idx < static_cast<int>(dp[done].size()); ++idx) {
            long long current = dp[done][idx];
            if (current >= INF) {
                continue;
            }

            // If producing more credits separately would dominate this state, skip it.
            if (idx > 0 && current - dp[done][0] >= credit_cost[idx]) {
                continue;
            }

            int credit = min_credit[done] + idx;
            int upper = min(n, done + c);
            int extra = -1;
            for (int next = upper; next > done; --next, ++extra) {
                int next_credit = credit + extra;
                if (next_credit > max_credit[next]) {
                    break;
                }
                int helpers = extra + 1;
                long long transition_cost = t[n - 1 - done] + fast_prefix[helpers];
                int next_idx = next_credit - min_credit[next];
                dp[next][next_idx] = min(dp[next][next_idx], current + transition_cost);
            }
        }
    }

    long long answer = INF;
    for (int credit = min_credit[n]; credit <= -1; ++credit) {
        int idx = credit - min_credit[n];
        answer = min(answer, dp[n][idx] + credit_cost[-1 - credit]);
    }
    cout << answer << '\n';
}

}  // namespace

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    solve();
    return 0;
}

Source Files and Assets

Raw files are still available here when you want the original TeX, C++, or statement assets.

Show raw files