use rand::prelude::{StdRng, SliceRandom}; use rand::{SeedableRng, Rng, thread_rng}; pub const SIZE: usize = 16384; pub struct City { prices: Vec, buyable_house_count: usize } impl City { pub fn read_from_file(filename: &str) -> Self { let values = std::fs::read(filename).unwrap(); let mut prices: Vec = Vec::new(); for y in 0..SIZE { for x in 0..SIZE { let price = (values[(y * SIZE + x) * 2] as u16) | ((values[(y * SIZE + x) * 2 + 1] as u16) << 8); prices.push(price); } } City::new(prices) } pub fn new(prices: Vec) -> Self { let mut buyable_house_count = 0; for &price in &prices { if price > 0 { buyable_house_count += 1; } } City { prices, buyable_house_count } } pub fn get_price(&self, house: &House) -> u16 { self.prices[house.y * SIZE + house.x] } pub fn get_price_xy(&self, x: usize, y: usize) -> u16 { self.prices[y * SIZE + x] } pub fn is_house(&self, house: &House) -> bool { self.get_price(&house) > 0 } pub fn is_house_xy(&self, x: usize, y: usize) -> bool { self.get_price_xy(x, y) > 0 } pub fn get_house_count(&self) -> usize { self.buyable_house_count } } #[derive(Eq, PartialEq, Hash, Copy, Clone)] pub struct House { x: usize, y: usize, } impl House { pub fn new(x: usize, y: usize) -> Self { House { x, y } } } struct HouseLayout<'a> { city: &'a City, reachable: Vec, houses: Vec, reachable_houses: usize } impl<'a> HouseLayout<'a> { pub fn new(city: &'a City) -> Self { HouseLayout { city, reachable: vec![0; SIZE * SIZE], houses: Vec::new(), reachable_houses: 0 } } pub fn cover_count(&self, house: House) -> u16 { self.reachable[house.y * SIZE + house.x] } pub fn is_covered(&self, house: House) -> bool { self.cover_count(house) > 0 } pub fn add_house(&mut self, house: House) -> usize { for y in (house.y as i32 - 500).max(0)..=(house.y as i32 + 500).min((SIZE - 1) as i32) { for x in (house.x as i32 - 500).max(0)..=(house.x as i32 + 500).min((SIZE - 1) as i32) { let index = y as usize * SIZE + x as usize; if self.reachable[index] == 0 && self.city.is_house_xy(x as usize, y as usize) { self.reachable_houses += 1; } self.reachable[index] += 1; } } self.houses.push(house); self.houses.len() - 1 } pub fn remove_house(&mut self, index: usize) { let house = self.houses.swap_remove(index); for y in (house.y as i32 - 500).max(0)..=(house.y as i32 + 500).min((SIZE - 1) as i32) { for x in (house.x as i32 - 500).max(0)..=(house.x as i32 + 500).min((SIZE - 1) as i32) { let index = y as usize * SIZE + x as usize; self.reachable[index] -= 1; if self.reachable[index] == 0 && self.city.is_house_xy(x as usize, y as usize) { self.reachable_houses -= 1; } } } } pub fn is_valid(&self) -> bool { self.reachable_houses == self.city.buyable_house_count } pub fn price(&self) -> u32 { get_price(self.city, &self.houses) } pub fn houses(&self) -> &Vec { &self.houses } } fn main() { let city = City::read_from_file("01.in"); const AROUND_RANGE: i32 = 200; const MAX_CANDIDATES: usize = 200; //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(); eprintln!("Starting seed {}", seed); let mut rng = StdRng::seed_from_u64(seed); let mut layout = HouseLayout::new(&city); loop { loop { let x = rng.gen_range(0..SIZE); let y = rng.gen_range(0..SIZE); let house = House::new(x, y); if city.is_house_xy(x, y) && !layout.is_covered(house) { layout.add_house(house); break; } } if layout.is_valid() { break; } } eprintln!("Finished random init, price: {}", layout.price()); let mut untried_houses = layout.houses().clone(); untried_houses.shuffle(&mut rng); while untried_houses.len() > 0 { let house = untried_houses.pop().unwrap(); let mut house_index = layout.houses().iter().position(|x| *x == house).unwrap(); 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; 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)); } } } 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"); } else { for (i, &candidate) in new_candidates.iter().enumerate() { if i > MAX_CANDIDATES { //eprintln!("No valid candidate"); break; } //eprint!("Found candidate {}...", i); let old_price = layout.price(); layout.remove_house(house_index); if layout.is_valid() { // 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!("Improved price: {}", new_price); untried_houses = layout.houses().clone(); untried_houses.shuffle(&mut rng); break; } let candidate_index = layout.add_house(candidate); 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!("Improved price: {}", new_price); untried_houses = layout.houses().clone(); untried_houses.shuffle(&mut rng); break; } else { //eprintln!(" candidate is invalid."); layout.remove_house(candidate_index); house_index = layout.add_house(house); } } } } let price = layout.price(); if best_price.is_none() || price < best_price.unwrap() { best_price = Some(price); eprintln!("Finished randomization, price: {}, new best, printing", price); println!("Price {}, seed {}", price, seed); print_houses(&layout.houses()); println!(); } else { eprintln!("Finished randomization, price: {}, printing", price); println!("Price {}, seed {}", price, seed); print_houses(&layout.houses()); println!(); } } } pub fn get_neighbors(city: &City, house: &House) -> Vec { let mut neighbors = Vec::new(); for y in (house.y as i32 - 500).max(0)..=(house.y as i32 + 500).min((SIZE - 1) as i32) { for x in (house.x as i32 - 500).max(0)..=(house.x as i32 + 500).min((SIZE - 1) as i32) { let house = House::new(x as usize, y as usize); if city.get_price(&house) > 0 { neighbors.push(house); } } } neighbors } fn print_houses(houses: &Vec) { println!("{}", houses.len()); for house in houses { println!("{} {}", house.y, house.x); } } fn get_price(city: &City, houses: &Vec) -> u32 { let mut price = 0u32; for house in houses { price += city.get_price(&house) as u32; } price } fn is_valid(city: &City, houses: &Vec) -> Option { let mut reachable = vec![false; SIZE * SIZE]; let mut price = 0u32; for house in houses { assert!(city.prices[house.y * SIZE + house.x] > 0); for y in (house.y as i32 - 500).max(0)..=(house.y as i32 + 500).min((SIZE - 1) as i32) { for x in (house.x as i32 - 500).max(0)..=(house.x as i32 + 500).min((SIZE - 1) as i32) { reachable[y as usize * SIZE + x as usize] = true; } } price += city.get_price(&house) as u32; } for y in 0..SIZE { for x in 0..SIZE { if !reachable[y * SIZE + x] && city.prices[y * SIZE + x] > 0 { return None; } } } Some(price) }