pub fn knapsack01(values: &[i64], weights: &[i64], capacity: i64) -> i64 {
let cap = capacity as usize;
let mut best = vec![0i64; cap + 1];
for (i, &w) in weights.iter().enumerate() {
let w = w as usize;
for c in (w..=cap).rev() {
best[c] = best[c].max(best[c - w] + values[i]);
}
}
best[cap]
}
pub fn knapsack_bounded(values: &[i64], weights: &[i64], counts: &[i64], capacity: i64) -> i64 {
let cap = capacity as usize;
let mut best = vec![0i64; cap + 1];
for i in 0..values.len() {
for _ in 0..counts[i] {
let w = weights[i] as usize;
for c in (w..=cap).rev() {
best[c] = best[c].max(best[c - w] + values[i]);
}
}
}
best[cap]
}
pub fn subset_sum(weights: &[i64], target: i64) -> bool {
let t = target as usize;
let mut reachable = vec![false; t + 1];
reachable[0] = true;
for &w in weights {
let w = w as usize;
if w > t {
continue;
}
for c in (w..=t).rev() {
if reachable[c - w] {
reachable[c] = true;
}
}
}
reachable[t]
}
pub fn assignment_min(costs: &[Vec<i64>]) -> i64 {
let n = costs.len();
assert!(n <= 8, "brute force assignment limited to n <= 8");
let mut perm: Vec<usize> = (0..n).collect();
let mut best = i64::MAX;
permute(&mut perm, 0, &mut |p| {
let cost: i64 = p.iter().enumerate().map(|(i, &j)| costs[i][j]).sum();
if cost < best {
best = cost;
}
});
best
}
fn permute(items: &mut Vec<usize>, k: usize, visit: &mut impl FnMut(&[usize])) {
if k == items.len() {
visit(items);
return;
}
for i in k..items.len() {
items.swap(k, i);
permute(items, k + 1, visit);
items.swap(k, i);
}
}
pub fn coin_change_min(denoms: &[i64], target: i64) -> Option<i64> {
let t = target as usize;
let mut best = vec![i64::MAX; t + 1];
best[0] = 0;
for c in 1..=t {
for &d in denoms {
let d = d as usize;
if d <= c && best[c - d] != i64::MAX {
best[c] = best[c].min(best[c - d] + 1);
}
}
}
(best[t] != i64::MAX).then_some(best[t])
}
pub fn fixed_charge_min(
open_cost: &[i64],
unit_cost: &[i64],
cap: &[i64],
demand: i64,
) -> Option<i64> {
let n = open_cost.len();
assert!(n <= 16, "open-set enumeration limited to n <= 16");
let mut best: Option<i64> = None;
for mask in 0u32..(1 << n) {
let total_cap: i64 = (0..n)
.filter(|&i| mask & (1 << i) != 0)
.map(|i| cap[i])
.sum();
if total_cap < demand {
continue;
}
let mut open: Vec<usize> = (0..n).filter(|&i| mask & (1 << i) != 0).collect();
open.sort_by_key(|&i| unit_cost[i]);
let mut left = demand;
let mut cost: i64 = open.iter().map(|&i| open_cost[i]).sum();
for &i in &open {
let take = left.min(cap[i]);
cost += take * unit_cost[i];
left -= take;
if left == 0 {
break;
}
}
best = Some(match best {
None => cost,
Some(b) => b.min(cost),
});
}
best
}
pub struct TinyIlp {
pub bounds: Vec<(i64, i64)>,
pub objective: Vec<i64>,
pub constraints: Vec<(Vec<i64>, i8, i64)>,
pub maximize: bool,
}
impl TinyIlp {
pub fn brute_force(&self) -> Option<i64> {
let n = self.bounds.len();
let mut point: Vec<i64> = self.bounds.iter().map(|b| b.0).collect();
let mut best: Option<i64> = None;
loop {
let feasible = self.constraints.iter().all(|(coeffs, op, rhs)| {
let lhs: i64 = coeffs.iter().zip(&point).map(|(c, x)| c * x).sum();
match op {
-1 => lhs <= *rhs,
0 => lhs == *rhs,
1 => lhs >= *rhs,
_ => unreachable!(),
}
});
if feasible {
let obj: i64 = self.objective.iter().zip(&point).map(|(c, x)| c * x).sum();
best = Some(match best {
None => obj,
Some(b) if self.maximize => b.max(obj),
Some(b) => b.min(obj),
});
}
let mut i = 0;
loop {
if i == n {
return best;
}
if point[i] < self.bounds[i].1 {
point[i] += 1;
break;
}
point[i] = self.bounds[i].0;
i += 1;
}
}
}
}