Skip to content

Commit e2e5c09

Browse files
committed
LC 2463. Minimum Total Distance Traveled (Rust)
1 parent e863518 commit e2e5c09

File tree

2 files changed

+147
-0
lines changed

2 files changed

+147
-0
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,7 @@ to the solution in this repository.
713713
| [2444. Count Subarrays With Fixed Bounds][lc2444] | 🔴 Hard | [![python](res/py.png)][lc2444py] [![rust](res/rs.png)][lc2444rs] |
714714
| [2448. Minimum Cost to Make Array Equal][lc2448] | 🔴 Hard | [![rust](res/rs.png)][lc2448rs] |
715715
| [2462. Total Cost to Hire K Workers][lc2462] | 🟠 Medium | [![python](res/py.png)][lc2462py] |
716+
| [2463. Minimum Total Distance Traveled][lc2463] | 🔴 Hard | [![rust](res/rs.png)][lc2463rs] |
716717
| [2466. Count Ways To Build Good Strings][lc2466] | 🟠 Medium | [![rust](res/rs.png)][lc2466rs] |
717718
| [2477. Minimum Fuel Cost to Report to the Capital][lc2477] | 🟠 Medium | [![python](res/py.png)][lc2477py] [![rust](res/rs.png)][lc2477rs] |
718719
| [2481. Minimum Cuts to Divide a Circle][lc2481] | 🟢 Easy | [![python](res/py.png)][lc2481py] |
@@ -2351,6 +2352,8 @@ to the solution in this repository.
23512352
[lc2448rs]: leetcode/minimum-cost-to-make-array-equal.rs
23522353
[lc2462]: https://leetcode.com/problems/total-cost-to-hire-k-workers/
23532354
[lc2462py]: leetcode/total-cost-to-hire-k-workers.py
2355+
[lc2463]: https://leetcode.com/problems/minimum-total-distance-traveled/
2356+
[lc2463rs]: leetcode/minimum-total-distance-traveled.rs
23542357
[lc2466]: https://leetcode.com/problems/count-ways-to-build-good-strings/
23552358
[lc2466rs]: count-ways-to-build-good-strings.rs
23562359
[lc2477]: https://leetcode.com/problems/minimum-fuel-cost-to-report-to-the-capital/
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// 2463. Minimum Total Distance Traveled
2+
// 🔴 Hard
3+
//
4+
// https://leetcode.com/problems/minimum-total-distance-traveled/
5+
//
6+
// Tags: Array - Dynamic Programming - Sorting
7+
8+
use std::{cmp::Reverse, collections::BinaryHeap};
9+
10+
struct Solution;
11+
impl Solution {
12+
/// This solution does not work, I assumed that greedily choosing the closest factory would
13+
/// result in the most optimal result, but it is not the case, there is one test case that
14+
/// shows this.
15+
///
16+
/// Time complexity: O(m*log(m)+m*log(n)) - First we sort the robots, then we process them in
17+
/// order, for each, we pick the closets factory that can fix robots in O(log(n))
18+
/// Space complexity: O(m) - The heaps.
19+
#[allow(dead_code)]
20+
pub fn minimum_total_distance_greedy_heaps(mut robot: Vec<i32>, factory: Vec<Vec<i32>>) -> i64 {
21+
robot.sort_unstable();
22+
// Two heaps to find the closest factory to any robot.
23+
let mut left = BinaryHeap::<(i32, i32)>::new();
24+
let mut right = factory
25+
.iter()
26+
.map(|v| (Reverse((v[0], v[1]))))
27+
.collect::<BinaryHeap<_>>();
28+
let mut res = 0;
29+
for r in robot {
30+
while let Some(&Reverse((position, _capacity))) = right.peek() {
31+
// Move factories to the left/same spot to the min heap.
32+
if position > r {
33+
break;
34+
}
35+
left.push(right.pop().unwrap().0);
36+
}
37+
// Find out which factory is closer.
38+
let go_right = match (left.peek(), right.peek()) {
39+
(None, Some(_)) => true,
40+
(Some(_), None) => false,
41+
(Some(&(left_pos, _)), Some(&Reverse((right_pos, _)))) => {
42+
// If the distance to the right factory is closer.
43+
(r - left_pos).abs() > (right_pos - r).abs()
44+
}
45+
(None, None) => unreachable!("The problem guarantees enough factory capacity"),
46+
};
47+
if go_right {
48+
let (pos, capacity) = right.pop().unwrap().0;
49+
res += (pos - r).abs() as i64;
50+
// println!("Robot {} going right to {}. Total {}", r, pos, res);
51+
if capacity > 1 {
52+
right.push(Reverse((pos, capacity - 1)));
53+
}
54+
} else {
55+
let (pos, capacity) = left.pop().unwrap();
56+
res += (r - pos).abs() as i64;
57+
// println!("Robot {} going left to {}. Total {}", r, pos, res);
58+
if capacity > 1 {
59+
left.push((pos, capacity - 1));
60+
}
61+
}
62+
}
63+
res
64+
}
65+
66+
/// Use dynamic programming. See the explanation on the problem editorial.
67+
///
68+
/// Time complexity: O(m*n*k) - The nested loops.
69+
/// Space complexity: O(m) - The dp vector.
70+
///
71+
/// Runtime 4 ms Beats 100%
72+
/// Memory 2.20 MB Beats 100%
73+
pub fn minimum_total_distance(mut robot: Vec<i32>, mut factory: Vec<Vec<i32>>) -> i64 {
74+
robot.sort_unstable();
75+
factory.sort_unstable();
76+
let (m, n) = (robot.len(), factory.len());
77+
let mut dp = vec![i64::MAX; m + 1];
78+
dp[m] = 0;
79+
for fidx in (0..n).rev() {
80+
for ridx in 0..m {
81+
let mut cur = 0;
82+
// The number of robots that this factory can fix min the number of robots needing
83+
// fixing still.
84+
for k in 1..=(factory[fidx][1] as usize).min(m - ridx) as usize {
85+
cur += (robot[ridx + k - 1] - factory[fidx][0]).abs() as i64;
86+
dp[ridx] = dp[ridx].min(if dp[ridx + k] == i64::MAX {
87+
i64::MAX
88+
} else {
89+
dp[ridx + k] + cur
90+
});
91+
}
92+
// println!("{:?}", dp);
93+
}
94+
}
95+
dp[0]
96+
}
97+
}
98+
99+
// Tests.
100+
fn main() {
101+
let tests = [
102+
(vec![0, 4, 6], vec![vec![2, 2], vec![6, 2]], 4),
103+
(vec![1, -1], vec![vec![-2, 1], vec![2, 1]], 2),
104+
// This test fails on the greedy/heap solution, it is better for 9 to go to 7 even though
105+
// it is further.
106+
(
107+
vec![9, 11, 99, 101],
108+
vec![
109+
vec![10, 1],
110+
vec![7, 1],
111+
vec![14, 1],
112+
vec![100, 1],
113+
vec![96, 1],
114+
vec![103, 1],
115+
],
116+
6,
117+
),
118+
];
119+
println!("\n\x1b[92m» Running {} tests...\x1b[0m", tests.len());
120+
let mut success = 0;
121+
for (i, t) in tests.iter().enumerate() {
122+
let res = Solution::minimum_total_distance(t.0.clone(), t.1.clone());
123+
if res == t.2 {
124+
success += 1;
125+
println!("\x1b[92m✔\x1b[95m Test {} passed!\x1b[0m", i);
126+
} else {
127+
println!(
128+
"\x1b[31mx\x1b[95m Test {} failed expected: {:?} but got {}!!\x1b[0m",
129+
i, t.2, res
130+
);
131+
}
132+
}
133+
println!();
134+
if success == tests.len() {
135+
println!("\x1b[30;42m✔ All tests passed!\x1b[0m")
136+
} else if success == 0 {
137+
println!("\x1b[31mx \x1b[41;37mAll tests failed!\x1b[0m")
138+
} else {
139+
println!(
140+
"\x1b[31mx\x1b[95m {} tests failed!\x1b[0m",
141+
tests.len() - success
142+
)
143+
}
144+
}

0 commit comments

Comments
 (0)