pub fn min_merge_cost_knuth(weights: &[usize]) -> (usize, Vec<Vec<usize>>) {
let n = weights.len();
if n == 0 {
return (0, Vec::new());
}
if n == 1 {
return (0, vec![vec![0; 1]; 1]); }
let mut prefix_sum = vec![0; n + 1];
for i in 0..n {
prefix_sum[i + 1] = prefix_sum[i] + weights[i];
}
let mut dp = vec![vec![0_usize; n]; n];
let mut opt = vec![vec![0_usize; n]; n];
for i in 0..n {
dp[i][i] = 0;
opt[i][i] = i;
}
let cost = |i: usize, j: usize| prefix_sum[j + 1] - prefix_sum[i];
for length in 2..=n {
for i in 0..=n - length {
let j = i + length - 1;
let start_k = opt[i][j.saturating_sub(1)].max(i);
let end_k = opt
.get(i + 1)
.map(|row| row.get(j).cloned().unwrap_or(j.saturating_sub(1)))
.unwrap_or(j.saturating_sub(1))
.min(j.saturating_sub(1));
let mut min_val = usize::MAX;
let mut best_k = start_k;
for k in start_k..=end_k {
let val = dp[i][k] + dp[k + 1][j];
if val < min_val {
min_val = val;
best_k = k;
}
}
dp[i][j] = min_val + cost(i, j);
opt[i][j] = best_k;
}
}
(dp[0][n - 1], opt)
}
pub fn reconstruct_optimal_merge(
opt: &Vec<Vec<usize>>,
i: usize,
j: usize,
) -> Vec<(usize, usize, usize)> {
let mut result = Vec::new();
reconstruct_optimal_merge_rec(opt, i, j, &mut result);
result
}
fn reconstruct_optimal_merge_rec(
opt: &Vec<Vec<usize>>,
i: usize,
j: usize,
merges: &mut Vec<(usize, usize, usize)>,
) {
if i >= j {
return;
}
let k = opt[i][j];
merges.push((i, k, j));
reconstruct_optimal_merge_rec(opt, i, k, merges);
reconstruct_optimal_merge_rec(opt, k + 1, j, merges);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_single_file() {
let weights = vec![10];
let (cost, _opt) = min_merge_cost_knuth(&weights);
assert_eq!(cost, 0, "Merging 1 file is zero cost");
}
#[test]
fn test_two_files() {
let weights = vec![10, 20];
let (cost, _opt) = min_merge_cost_knuth(&weights);
assert_eq!(cost, 30);
}
#[test]
fn test_three_files() {
let weights = vec![10, 20, 30];
let (cost, opt) = min_merge_cost_knuth(&weights);
assert_eq!(cost, 90);
let merges = reconstruct_optimal_merge(&opt, 0, weights.len() - 1);
assert_eq!(merges.len(), 2);
}
#[test]
fn test_four_files() {
let weights = vec![1, 2, 3, 4];
let (cost, _opt) = min_merge_cost_knuth(&weights);
assert_eq!(cost, 19);
}
}