From 9fda072a1393a9ea604fd38bda999ffe25af3438 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Sejkora?= Date: Wed, 6 Jan 2021 19:27:15 +0100 Subject: [PATCH] Optimize candidate search --- src/main.rs | 113 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 93 insertions(+), 20 deletions(-) diff --git a/src/main.rs b/src/main.rs index dd52a93..1732f2d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,7 @@ use rand::prelude::{StdRng, SliceRandom}; use rand::{SeedableRng, Rng, thread_rng}; +use std::fmt; +use std::fmt::Formatter; pub const SIZE: usize = 16384; pub const HOUSE_RANGE: usize = 500; @@ -87,7 +89,13 @@ pub struct Rectangle { bottom: usize, } -struct HouseLayout<'a> { +impl fmt::Display for Rectangle { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "L{}-{}R T{}-{}B", self.left, self.right, self.top, self.bottom) + } +} + +pub struct HouseLayout<'a> { city: &'a City, reachable: Vec, houses: Vec, @@ -103,6 +111,10 @@ impl<'a> HouseLayout<'a> { self.reachable[house.y * SIZE + house.x] } + pub fn cover_count_xy(&self, x: usize, y: usize) -> u16 { + self.reachable[y * SIZE + x] + } + pub fn is_covered(&self, house: House) -> bool { self.cover_count(house) > 0 } @@ -157,12 +169,6 @@ impl<'a> HouseLayout<'a> { fn main() { let city = City::read_from_file("01.in"); - const AROUND_RANGE: i32 = 50; - const MAX_CANDIDATES: usize = 50; - //const MAX_FAILED_ITERATIONS: usize = 50; - println!("Params: AROUND_RANGE {}, MAX_CANDIDATES {}", AROUND_RANGE, MAX_CANDIDATES); - eprintln!("Params: AROUND_RANGE {}, MAX_CANDIDATES {}", AROUND_RANGE, MAX_CANDIDATES); - let mut best_price: Option = None; loop { let seed: u64 = thread_rng().gen(); @@ -195,11 +201,25 @@ fn main() { let house = untried_houses.pop().unwrap(); let mut house_index = layout.houses().iter().position(|x| *x == house).unwrap(); + let move_rectangle = match get_valid_move_rectangle(&layout, house) { + Ok(move_rectangle) => move_rectangle, + Err(RectangleSearchError::UselessHouse) => { + let old_price = layout.price(); + layout.remove_house(house_index); + let new_price = layout.price(); + let price_diff = new_price as i64 - old_price as i64; + //eprintln!(" candidate is valid, price diff: {}.", price_diff); + eprintln!("Removed a house (useless), diff {}", price_diff); + eprintln!("Improved price: {}", new_price); + untried_houses = layout.houses().clone(); + untried_houses.shuffle(&mut rng); + continue; + } + }; + let mut new_candidates = Vec::new(); - for delta_y in -AROUND_RANGE..=AROUND_RANGE { - for delta_x in -AROUND_RANGE..=AROUND_RANGE { - let new_x = (house.x as i32 + delta_x).max(0).min(SIZE as i32 - 1) as usize; - let new_y = (house.y as i32 + delta_y).max(0).min(SIZE as i32 - 1) as usize; + for new_y in move_rectangle.top..=move_rectangle.bottom { + for new_x in move_rectangle.left..=move_rectangle.right { if city.is_house_xy(new_x, new_y) && city.get_price_xy(new_x, new_y) < city.get_price(&house) { new_candidates.push(House::new(new_x, new_y)); } @@ -208,15 +228,11 @@ fn main() { new_candidates.sort_by(|a, b| city.get_price(&a).cmp(&city.get_price(&b))); if new_candidates.len() == 0 { - //eprintln!("Did not find candidate"); + eprintln!("Did not find candidate"); } else { for (i, &candidate) in new_candidates.iter().enumerate() { - if i > MAX_CANDIDATES { - //eprintln!("No valid candidate"); - break; - } - //eprint!("Found candidate {}...", i); + eprint!("Found candidate {}...", i); let old_price = layout.price(); layout.remove_house(house_index); @@ -225,8 +241,16 @@ fn main() { // The candidate is not needed, the house was unnecessary let new_price = layout.price(); let price_diff = new_price as i64 - old_price as i64; - //eprintln!(" candidate is valid, price diff: {}.", price_diff); eprintln!("Removed a house, diff {}", price_diff); + eprintln!("Move rectangle: {}", move_rectangle); + let house_rect = house.range_rectangle(); + eprintln!("Printing map in rect around house: {}", house_rect); + for y in house_rect.top..=house_rect.bottom { + for x in house_rect.left..=house_rect.right { + eprint!("{}", layout.cover_count_xy(x, y)); + } + eprintln!(); + } eprintln!("Improved price: {}", new_price); untried_houses = layout.houses().clone(); untried_houses.shuffle(&mut rng); @@ -238,13 +262,13 @@ fn main() { if layout.is_valid() { let new_price = layout.price(); let price_diff = new_price as i64 - old_price as i64; - //eprintln!(" candidate is valid, price diff: {}.", price_diff); + eprintln!(" candidate is valid, price diff: {}.", price_diff); eprintln!("Improved price: {}", new_price); untried_houses = layout.houses().clone(); untried_houses.shuffle(&mut rng); break; } else { - //eprintln!(" candidate is invalid."); + eprintln!(" candidate is invalid."); layout.remove_house(candidate_index); house_index = layout.add_house(house); } @@ -268,6 +292,55 @@ fn main() { } } +pub enum RectangleSearchError { + UselessHouse +} + +pub fn get_valid_move_rectangle(layout: &HouseLayout, house: House) -> Result { + // We first establish a bounding box for an that has to be covered if the house is removed. + let mut covered_rect: Option = None; + + let range_rect = house.range_rectangle(); + for y in range_rect.top..=range_rect.bottom { + for x in range_rect.left..=range_rect.right { + if layout.cover_count_xy(x, y) == 1 && layout.city.is_house_xy(x, y) { + // This house is only covered by the house, it has to be covered from the new position as well. + if let Some(cover) = &mut covered_rect { + cover.left = cover.left.min(x); + cover.right = cover.right.max(x); + cover.top = cover.top.min(y); + cover.bottom = cover.bottom.max(y); + } else { + covered_rect = Some(Rectangle { left: x, right: x, top: y, bottom: y }); + } + } + } + } + + if covered_rect.is_none() { + return Err(RectangleSearchError::UselessHouse) + } + + let covered_rect = covered_rect.unwrap(); + + // The distance of the rectangle from the original box tells us how much the house can move. + let dist_left = covered_rect.left - range_rect.left; + let dist_right = range_rect.right - covered_rect.right; + let dist_top = covered_rect.top - range_rect.top; + let dist_bottom = range_rect.bottom - covered_rect.bottom; + + let left = if house.x <= dist_right { 0 } else { house.x - dist_right }; + let right = if house.x >= SIZE - 1 - dist_left { SIZE - 1 } else { house.x + dist_left }; + let top = if house.y <= dist_bottom { 0 } else { house.y - dist_bottom }; + let bottom = if house.y >= SIZE - 1 - dist_top { SIZE - 1 } else { house.y + dist_top }; + + let valid_move_rectangle = Rectangle { + left, right, top, bottom + }; + + Ok(valid_move_rectangle) +} + pub fn get_neighbors(city: &City, house: &House) -> Vec { let mut neighbors = Vec::new(); let range_rect = house.range_rectangle();