diff --git a/src/combine-layouts.rs b/src/combine-layouts.rs index 773ed0f..681ac3a 100644 --- a/src/combine-layouts.rs +++ b/src/combine-layouts.rs @@ -15,14 +15,11 @@ fn main() { let layouts = db.layouts(); let sorted: Vec<_> = layouts.iter().sorted_by(|x, y| city::get_price(&city, x.houses()).cmp(&city::get_price(&city, y.houses()))).collect(); - let first = sorted[1]; - let second = sorted[0]; - eprintln!("Combining layouts (ID {}, price {}) and (ID {}, price {})", - first.id(), - city::get_price(&city, first.houses()), - second.id(), - city::get_price(&city, second.houses()), - ); - combine::try_combine(&city, first.houses(), second.houses()); + const TOP_LAYOUT_COUNT: usize = 10; + + let chosen_layouts = sorted.iter().take(TOP_LAYOUT_COUNT).map(|l| l.houses()).collect(); + eprintln!("Starting to combine {} top houses DB; vertical cuts", TOP_LAYOUT_COUNT); + + combine::try_combine(&city, &chosen_layouts, &chosen_layouts); } \ No newline at end of file diff --git a/src/combine.rs b/src/combine.rs index 3c0781c..639ce6d 100644 --- a/src/combine.rs +++ b/src/combine.rs @@ -2,36 +2,52 @@ use crate::city; use crate::city::{City, House, SIZE, HOUSE_RANGE}; use itertools::Itertools; use std::collections::VecDeque; +use std::collections::vec_deque::Iter; -pub fn try_combine(city: &City, layout1: &Vec, layout2: &Vec) { +pub fn try_combine(city: &City, left_layouts: &Vec<&Vec>, right_layouts: &Vec<&Vec>) { // Sorted in reverse so we can remove from the end - let mut houses1_sorted: Vec = layout1.iter().sorted_by(|h1, h2| h2.x.cmp(&h1.x)).map(|x| *x).collect(); - let mut houses2_sorted: Vec = layout2.iter().sorted_by(|h1, h2| h2.x.cmp(&h1.x)).map(|x| *x).collect(); - - // TODO: We may want to maintain K left sides and K right sides to compare K^2 layouts at once at each x - - // houses1 is left, houses2 is right - - let mut left = LeftLine::new(); - let mut right = RightLine::new(); + let mut left_houses_sorted: Vec> = left_layouts.iter() + .map(|l| l.iter().sorted_by(|h1, h2| h2.x.cmp(&h1.x)).map(|x| *x).collect()) + .collect(); + let mut right_houses_sorted: Vec> = right_layouts.iter() + .map(|l| l.iter().sorted_by(|h1, h2| h2.x.cmp(&h1.x)).map(|x| *x).collect()) + .collect(); + + let mut lefts = Vec::new(); + let mut rights = Vec::new(); + + for _ in left_layouts { + lefts.push(LeftLine::new()); + } - // Make sure that we include all houses initially - while let Some(house) = houses2_sorted.pop() { - right.add_house(house); + for mut right_layout in right_houses_sorted { + let mut right = RightLine::new(); + // Make sure that we include all houses initially + while let Some(house) = right_layout.pop() { + right.add_house(house, &city); + } + rights.push(right); } + let mut best_price = None; // x is the last left coordinate, x+1 is right for x in 0..SIZE { + eprintln!("Starting x {}", x); // Update the lines - while let Some(house) = houses1_sorted.last() { - if house.x == x { - left.add_house(*house); - houses1_sorted.pop(); - } else { - break; + for (mut left_line, mut left_houses) in lefts.iter_mut().zip(left_houses_sorted.iter_mut()) { + while let Some(house) = left_houses.last() { + if house.x == x { + left_line.add_house(*house, &city); + left_houses.pop(); + } else { + break; + } } } - right.remove_houses(x); + + for mut right_line in rights.iter_mut() { + right_line.remove_houses(x, &city); + } // Check compatibility of lines if x == 0 { @@ -40,22 +56,35 @@ pub fn try_combine(city: &City, layout1: &Vec, layout2: &Vec) { continue; } - if is_compatible(city, &left, &right) { - eprintln!("Compatible on X {}", x); - let houses: Vec<_> = layout1.iter().filter(|h| h.x <= x).chain(layout2.iter().filter(|h| h.x > x)).map(|h| *h).collect(); - eprintln!("Price {}", city::get_price(&city, &houses)); - //if let Some(price) = city::is_valid(&city, &houses) { - // eprintln!("Merge valid with price {}", price) - //} else { - // eprintln!("Merge actually invalid, printing invalid merge"); - // println!("{}", houses.len()); - // for house in houses { - // println!("{} {}", house.y, house.x); - // } - //} - } else { - eprintln!("Incompatible on X {}", x); + let mut compatibles = 0; + let mut incompatibles = 0; + for (left_i, left) in lefts.iter().enumerate() { + for (right_i, right) in rights.iter().enumerate() { + if left_i == right_i { + // Do not compare the same layout + continue; + } + + if is_compatible(city, &left, &right) { + let price = left.price + right.price; + if best_price.is_none() || price < best_price.unwrap() { + best_price = Some(price); + eprintln!("{} - new best score, cut on x {}, left {} - right {}, printing", price, x, left_i, right_i); + println!("{} - new best score, cut on x {}, left {} - right {}", price, x, left_i, right_i); + let new_houses: Vec<_> = left.houses().chain(right.houses()).collect(); + println!("{}", new_houses.len()); + for house in new_houses { + println!("{} {}", house.y, house.x); + } + } + compatibles += 1; + } else { + incompatibles += 1; + } + } } + + eprintln!("{}/{} compatible", compatibles, compatibles + incompatibles); } } @@ -67,8 +96,6 @@ fn is_compatible(city: &City, left: &LeftLine, right: &RightLine) -> bool { // This range will often be empty for x in (max_left_covered_x+1)..min_right_covered_x { if city.is_house_xy(x, y) { - // This is an uncovered house - eprintln!("House ({},{}) in uncovered range [{},{}]", x, y, max_left_covered_x+1, min_right_covered_x-1); return false; } } @@ -78,53 +105,67 @@ fn is_compatible(city: &City, left: &LeftLine, right: &RightLine) -> bool { } struct LeftLine { - covers: Vec + covers: Vec, + houses: Vec, + price: u32 } struct RightLine { covers: Vec, - houses: VecDeque + houses: VecDeque, + price: u32 } impl LeftLine { pub fn new() -> Self { // XXX: Careful, default of 0 includes covering first vertical line let covers = vec![0; SIZE]; - LeftLine {covers} + let houses = Vec::new(); + LeftLine {covers, houses, price: 0 } } - pub fn add_house(&mut self, house: House) { + pub fn add_house(&mut self, house: House, city: &City) { let range_rect = house.range_rectangle(); for y in range_rect.top..=range_rect.bottom { // Should always be the max variant self.covers[y] = self.covers[y].max(range_rect.right); } + self.price += city.get_price(house) as u32; + self.houses.push(house); } pub fn get_max_covered_x(&self, y: usize) -> usize { self.covers[y] } + + pub fn get_side_price(&self) -> u32 { + self.price + } + + pub fn houses(&self) -> std::slice::Iter<'_, House> { + self.houses.iter() + } } impl RightLine { pub fn new() -> Self { let covers = vec![usize::MAX; SIZE]; let houses = VecDeque::new(); - RightLine {covers, houses} + RightLine {covers, houses, price: 0} } - pub fn add_house(&mut self, house: House) { + pub fn add_house(&mut self, house: House, city: &City) { // Added houses have to always be ordered by x - eprintln!("Added house ({},{}) to right line", house.x, house.y); let range_rect = house.range_rectangle(); for y in range_rect.top..=range_rect.bottom { self.covers[y] = self.covers[y].min(range_rect.left); } self.houses.push_back(house); + self.price += city.get_price(house) as u32 } - pub fn remove_houses(&mut self, x: usize) { + pub fn remove_houses(&mut self, x: usize, city: &City) { // Has to be called with x, x+1, x+2... while let Some(house) = self.houses.front() { if house.x == x { @@ -140,7 +181,6 @@ impl RightLine { // intersects the removed area for house in &self.houses { let house_rect = house.range_rectangle(); - // TODO: Verify this intersection is correct let y_intersection = if removed_house.y < house.y { house_rect.top..=removed_rect.bottom } else { @@ -151,6 +191,8 @@ impl RightLine { self.covers[y] = self.covers[y].min(house_rect.left); } } + + self.price -= city.get_price(removed_house) as u32; } else { break; } @@ -160,4 +202,12 @@ impl RightLine { pub fn get_min_covered_x(&self, y: usize) -> usize { self.covers[y].min(SIZE) } + + pub fn get_side_price(&self) -> u32 { + self.price + } + + pub fn houses(&self) -> Iter<'_, House> { + self.houses.iter() + } }