#3594
Hard Algorithms Minimum time to transport all individuals
Array Dynamic Programming Bit Manipulation Graph Theory Heap (Priority Queue) Shortest Path Bitmask
28.1% acceptance
Feb 25, 2026
41
6
You are given n individuals, boat capacity k, m cyclic stages with multipliers mul[j].
Groups cross in time = max(time[group]) * mul[stage]. Stage advances by floor(crossing_time) % m.
One person returns if individuals remain. Return min total time. -1 if impossible.
Solution
Rust
Time O(n * m)
Space O(n * m)
impl Solution {
pub fn min_time(n: i32, k: i32, m: i32, time: Vec<i32>, mul: Vec<f64>) -> f64 {
let n = n as usize;
let k = k as usize;
let m = m as usize;
// k == 1 and n > 1: impossible – sender must also return, making no progress
if k == 1 && n > 1 {
return -1.0;
}
// Dijkstra on state (mask, stage):
// mask – bit i set iff person i is already at the destination
// stage – current cyclic stage index (0..m-1)
// Each transition = one forward crossing (group ≤ k) + optional return trip.
// The boat is always at base at the start of every state so the pairing is valid.
//
// Distances are stored as i64-scaled integers (scale = 10^9) to avoid the
// float→int64→float round-trip precision loss that corrupts the pruning check.
use std::collections::BinaryHeap;
use std::cmp::Reverse;
let full_mask = (1usize << n) - 1;
// scale: 10^9 gives nanosecond precision; max time ≈ 100*2.0*12 trips ≈ 2400 → well within i64
const SCALE: i64 = 1_000_000_000;
let mut dist = vec![vec![i64::MAX; m]; 1 << n];
dist[0][0] = 0;
let mut heap: BinaryHeap<(Reverse<i64>, usize, usize)> = BinaryHeap::new();
heap.push((Reverse(0i64), 0usize, 0usize));
while let Some((Reverse(t_scaled), mask, stage)) = heap.pop() {
if mask == full_mask {
return t_scaled as f64 / SCALE as f64;
}
// Stale entry – a shorter path to (mask, stage) was already processed
if t_scaled > dist[mask][stage] { continue; }
// People still at base camp (bit not set in mask)
let base_mask: usize = (0..n)
.filter(|&i| (mask >> i) & 1 == 0)
.fold(0usize, |acc, i| acc | (1 << i));
if base_mask == 0 { return t_scaled as f64 / SCALE as f64; }
// Enumerate every non-empty subset of base people with size ≤ k
let mut sub = base_mask;
loop {
if sub != 0 && sub.count_ones() as usize <= k {
let max_t = (0..n)
.filter(|&i| (sub >> i) & 1 == 1)
.map(|i| time[i])
.max()
.unwrap() as f64;
let cross_t = max_t * mul[stage];
let cross_sc = (cross_t * SCALE as f64) as i64;
let new_stage = (stage + cross_t.floor() as usize) % m;
let new_mask = mask | sub;
if new_mask == full_mask {
// Last trip – no return needed
let nt = t_scaled + cross_sc;
if nt < dist[new_mask][new_stage] {
dist[new_mask][new_stage] = nt;
heap.push((Reverse(nt), new_mask, new_stage));
}
} else {
// Must send someone back; try every person currently at destination
let at_dest_mask = new_mask; // bits set = people at dest
for r in 0..n {
if (at_dest_mask >> r) & 1 == 0 { continue; }
let ret_t = time[r] as f64 * mul[new_stage];
let ret_sc = (ret_t * SCALE as f64) as i64;
let ret_stage = (new_stage + ret_t.floor() as usize) % m;
let fin_mask = new_mask & !(1 << r);
let total = t_scaled + cross_sc + ret_sc;
if total < dist[fin_mask][ret_stage] {
dist[fin_mask][ret_stage] = total;
heap.push((Reverse(total), fin_mask, ret_stage));
}
}
}
}
if sub == 0 { break; }
sub = (sub - 1) & base_mask;
}
}
// If full_mask was never reached, return -1
let best = dist[full_mask].iter().cloned().filter(|&v| v < i64::MAX).min();
match best {
Some(v) => v as f64 / SCALE as f64,
None => -1.0,
}
}
}