|
|
@ -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<House>, layout2: &Vec<House>) { |
|
|
|
pub fn try_combine(city: &City, left_layouts: &Vec<&Vec<House>>, right_layouts: &Vec<&Vec<House>>) { |
|
|
|
// Sorted in reverse so we can remove from the end
|
|
|
|
let mut houses1_sorted: Vec<House> = layout1.iter().sorted_by(|h1, h2| h2.x.cmp(&h1.x)).map(|x| *x).collect(); |
|
|
|
let mut houses2_sorted: Vec<House> = 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<Vec<House>> = 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<Vec<House>> = 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<House>, layout2: &Vec<House>) { |
|
|
|
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<usize> |
|
|
|
covers: Vec<usize>, |
|
|
|
houses: Vec<House>, |
|
|
|
price: u32 |
|
|
|
} |
|
|
|
|
|
|
|
struct RightLine { |
|
|
|
covers: Vec<usize>, |
|
|
|
houses: VecDeque<House> |
|
|
|
houses: VecDeque<House>, |
|
|
|
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() |
|
|
|
} |
|
|
|
} |
|
|
|