Řešení KSP úlohy 33-3-4 Obsazování území https://ksp.mff.cuni.cz/h/ulohy/33/zadani3.html#task-33-3-4
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

409 lines
13 KiB

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;
pub struct City {
prices: Vec<u16>,
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<u16> = 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<u16>) -> 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 }
}
pub fn range_rectangle(&self) -> Rectangle {
let top = if self.y <= HOUSE_RANGE { 0 } else { self.y - HOUSE_RANGE };
let bottom = if self.y >= SIZE - 1 - HOUSE_RANGE { SIZE - 1 } else { self.y + HOUSE_RANGE };
let left = if self.x <= HOUSE_RANGE { 0 } else { self.x - HOUSE_RANGE };
let right = if self.x >= SIZE - 1 - HOUSE_RANGE { SIZE - 1 } else { self.x + HOUSE_RANGE };
Rectangle {top, bottom, left, right}
}
}
/// Rectangle - a 2D range with inclusive bounds
pub struct Rectangle {
/// The smaller x coordinate.
left: usize,
/// The bigger x coordinate.
right: usize,
/// The smaller y coordinate.
top: usize,
/// The bigger y coordinate.
bottom: usize,
}
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<u16>,
houses: Vec<House>,
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 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
}
pub fn add_house(&mut self, house: House) -> usize {
let range_rect = house.range_rectangle();
for y in range_rect.top..=range_rect.bottom {
for x in range_rect.left..=range_rect.right {
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);
let range_rect = house.range_rectangle();
for y in range_rect.top..=range_rect.bottom {
for x in range_rect.left..=range_rect.right {
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<House> {
&self.houses
}
}
fn main() {
let city = City::read_from_file("01.in");
let mut best_price: Option<u32> = 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 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 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));
}
}
}
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() {
//eprint!("Found candidate {}...", i);
let old_price = layout.price();
layout.remove_house(house_index);
layout.add_house(candidate);
assert!(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;
}
}
}
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!("New best!");
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 enum RectangleSearchError {
UselessHouse
}
pub fn get_valid_move_rectangle(layout: &HouseLayout, house: House) -> Result<Rectangle, RectangleSearchError> {
// We first establish a bounding box for an that has to be covered if the house is removed.
let mut covered_rect: Option<Rectangle> = 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<House> {
let mut neighbors = Vec::new();
let range_rect = house.range_rectangle();
for y in range_rect.top..=range_rect.bottom {
for x in range_rect.left..=range_rect.right {
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<House>) {
println!("{}", houses.len());
for house in houses {
println!("{} {}", house.y, house.x);
}
}
fn get_price(city: &City, houses: &Vec<House>) -> u32 {
let mut price = 0u32;
for house in houses {
price += city.get_price(&house) as u32;
}
price
}
fn is_valid(city: &City, houses: &Vec<House>) -> Option<u32> {
let mut reachable = vec![false; SIZE * SIZE];
let mut price = 0u32;
for house in houses {
assert!(city.prices[house.y * SIZE + house.x] > 0);
let range_rect = house.range_rectangle();
for y in range_rect.top..=range_rect.bottom {
for x in range_rect.left..=range_rect.right {
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)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn house_rectangle_at_min() {
let house = House::new(0, 0);
let rect = house.range_rectangle();
assert_eq!(rect.top, 0);
assert_eq!(rect.left, 0);
assert_eq!(rect.right, HOUSE_RANGE);
assert_eq!(rect.bottom, HOUSE_RANGE);
}
#[test]
fn house_rectangle_at_max() {
let house = House::new(SIZE - 1, SIZE - 1);
let rect = house.range_rectangle();
assert_eq!(rect.top, SIZE - 1 - HOUSE_RANGE);
assert_eq!(rect.left, SIZE - 1 - HOUSE_RANGE);
assert_eq!(rect.right, SIZE - 1);
assert_eq!(rect.bottom, SIZE - 1);
}
#[test]
fn house_rect_in_middle() {
let house = House::new(SIZE / 2, SIZE / 2);
let rect = house.range_rectangle();
assert_eq!(rect.top, house.y - HOUSE_RANGE);
assert_eq!(rect.left, house.x - HOUSE_RANGE);
assert_eq!(rect.right, house.x + HOUSE_RANGE);
assert_eq!(rect.bottom, house.y + HOUSE_RANGE);
}
}