Greedy
Matching with 0..1 or 1..0 distribution
- Matching items with boxes.
- Maximize the total matched values.
- 0/1 indicates matching feasibility.
// Sorted Boxes
// A B C D
// item 0 (v0): 1 1
// item 1 (v1): 1 1 1
// item 2 (v2): 1
// item 3 (v3): 1 1 1 1
// Each row (item) is a 0...1 distribution.
fn greedy_packing_01(mut items: Vec<(i64, i64)>, boxes: Vec<i64>) -> i64 {
let mut boxes = BTreeSet::from_iter((0..boxes.len()).map(|i| (boxes[i], i)));
items.sort_by_key(|&(w, v)| (Reverse(v), w));
let mut ans = 0;
for &(w, v) in &items {
if let Some(&(b, i)) = boxes.range((w, 0)..).next() {
boxes.remove(&(b, i));
ans += v;
}
}
ans
}
// Sorted Boxes
// A B C D
// item 0 (v0): 1
// item 1 (v1): 1 1 1
// item 2 (v2): 1 1 1 1
// item 3 (v3): 1 1
// Each row (item) is a 1...0 distribution.
fn greedy_packing_10(mut items: Vec<(i64, i64)>, boxes: Vec<i64>) -> i64 {
let mut boxes = BTreeSet::from_iter((0..boxes.len()).map(|i| (boxes[i], i)));
items.sort_by_key(|&(w, v)| (std::cmp::Reverse(v), w));
let mut ans = 0;
for &(w, v) in &items {
if let Some(&(b, i)) = boxes.range(..=(w, !0)).last() {
boxes.remove(&(b, i));
ans += v;
}
}
ans
}
Given
Nitems with weightW[i]and valueV[i], andMboxes with sizeX[i], each box can contain at most 1 item thatw < x (or w >= x). What is the maximum total value that can be put in the boxes? (N < 1e5, M < 1e5)
Assume we've already found the maximum value achievable by packing items 0..i into the M available boxes. What's the optimal solution for packing items 0..=i into M boxes? With the addition of item i, the optimal solution comes from two scenarios
- There's an empty box: Place item
iinto an empty box that can accommodate it. If there are multiple such boxes, choose the one with the smallest capacity. - There are no empty boxes: Check if, among all boxes that could accommodate item
i, there's an itemjalready packed whose valueV[j]is less thanV[i]. If so, we replace itemjwith itemi.
Scenario 2 is tricky to implement directly. However, it only occurs when V[j] < V[i]. This observation leads to a crucial optimization: if we pre-sort the items by their value in descending order, we no longer need to consider scenario 2. This is because when we are processing item i, all items already packed in boxes will have a value greater than or equal to item i's value. In such a case, there's no reason to remove a higher-value item to make space for a lower-value item i.
Matching with Segments
- Each item's feasiblity is a range.
- The ranges are usually in a large space (> 10^9).
- Maximize the matching number.
Using sweep line from left to right, at each point, we choose a segment with minimum r from valid segments. Valid segments are maintained in a BTreeSet.
Note that the space is usually large, be careful about how t is updated.
// Boxes
// 0 1 2 3 4
// item 0: o---o
// item 1: o-----o
// item 2: o-o
// item 3: o---o
fn greedy_matching_segs(mut segs: Vec<(i64, i64)>) -> usize {
segs.sort();
let ls = BTreeSet::from_iter(segs.iter().map(|s| s.0));
let mut rs = BTreeSet::new();
let mut t = 0; // sweep line
let mut i = 0; // segs index
let mut ans = 0;
loop {
// Insert new valid segments to rs
while i < segs.len() && segs[i].0 <= t {
rs.insert((segs[i].1, i));
i += 1;
}
// Remove invalid segments from rs
while let Some(&(r, j)) = rs.first() {
if r < t {
rs.remove(&(r, j));
} else {
break;
}
}
// Try to pick 1 segment
if let Some(&(r, j)) = rs.first() {
// Successfully picked, t += 1
rs.remove(&(r, j));
ans += 1;
t += 1;
} else {
// Failed, jump to next possible time
if let Some(&l) = ls.range(t + 1..).next() {
t = l;
} else {
break;
}
}
}
ans
}