Ř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.

263 lines
7.2 KiB

use std::fmt;
use std::fmt::Formatter;
pub const INPUT_CITY_WIDTH: usize = 16384;
pub const INPUT_CITY_HEIGHT: usize = 16384;
pub const HOUSE_RANGE: usize = 500;
pub struct City {
prices: Vec<u16>,
buyable_house_count: usize,
width: usize,
height: usize
}
impl City {
pub fn read_from_file(filename: &str, width: usize, height: usize) -> Self {
let values = std::fs::read(filename).unwrap();
let mut prices: Vec<u16> = Vec::new();
for y in 0..height {
for x in 0..width {
let price = (values[(y * width + x) * 2] as u16) | ((values[(y * width + x) * 2 + 1] as u16) << 8);
prices.push(price);
}
}
City::new(prices, width, height)
}
pub fn new(prices: Vec<u16>, width: usize, height: usize) -> Self {
let mut buyable_house_count = 0;
for &price in &prices {
if price > 0 {
buyable_house_count += 1;
}
}
City { prices, buyable_house_count, width, height }
}
pub fn get_price(&self, house: House) -> u16 {
self.prices[house.y * self.width + house.x]
}
pub fn get_price_xy(&self, x: usize, y: usize) -> u16 {
self.prices[y * self.width + 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
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
}
#[derive(Eq, PartialEq, Hash, Copy, Clone)]
pub struct House {
pub x: usize,
pub y: usize,
}
impl House {
pub fn new(x: usize, y: usize) -> Self {
House { x, y }
}
pub fn range_rectangle(&self, city: &City) -> Rectangle {
let top = if self.y <= HOUSE_RANGE { 0 } else { self.y - HOUSE_RANGE };
let bottom = if self.y >= city.height() - 1 - HOUSE_RANGE { city.height() - 1 } else { self.y + HOUSE_RANGE };
let left = if self.x <= HOUSE_RANGE { 0 } else { self.x - HOUSE_RANGE };
let right = if self.x >= city.width() - 1 - HOUSE_RANGE { city.width() - 1 } else { self.x + HOUSE_RANGE };
Rectangle {top, bottom, left, right}
}
}
/// Rectangle - a 2D range with inclusive bounds
#[derive(Clone, Copy)]
pub struct Rectangle {
/// The smaller x coordinate.
pub left: usize,
/// The bigger x coordinate.
pub right: usize,
/// The smaller y coordinate.
pub top: usize,
/// The bigger y coordinate.
pub 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)
}
}
impl Rectangle {
pub fn is_inside(&self, x: usize, y: usize) -> bool {
self.left <= x && x <= self.right && self.top <= y && y <= self.bottom
}
pub fn width(&self) -> usize {
self.right - self.left
}
pub fn height(&self) -> usize {
self.bottom - self.top
}
}
pub struct HouseLayout<'a> {
pub 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; city.width() * city.height()], houses: Vec::new(), reachable_houses: 0 }
}
pub fn cover_count(&self, house: House) -> u16 {
self.reachable[house.y * self.city.width + house.x]
}
pub fn cover_count_xy(&self, x: usize, y: usize) -> u16 {
self.reachable[y * self.city.width + 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(self.city);
for y in range_rect.top..=range_rect.bottom {
for x in range_rect.left..=range_rect.right {
let index = y as usize * self.city.width + 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(self.city);
for y in range_rect.top..=range_rect.bottom {
for x in range_rect.left..=range_rect.right {
let index = y as usize * self.city.width + 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.get_house_count()
}
pub fn price(&self) -> u32 {
get_price(self.city, &self.houses)
}
pub fn houses(&self) -> &Vec<House> {
&self.houses
}
}
pub 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
}
pub fn is_valid(city: &City, houses: &Vec<House>) -> Option<u32> {
let mut reachable = vec![false; city.width() * city.height()];
let mut price = 0u32;
for house in houses {
assert!(city.is_house(*house));
let range_rect = house.range_rectangle(city);
for y in range_rect.top..=range_rect.bottom {
for x in range_rect.left..=range_rect.right {
reachable[y as usize * city.width() + x as usize] = true;
}
}
price += city.get_price(*house) as u32;
}
for y in 0..city.height {
for x in 0..city.width {
if !reachable[y * city.width + x] && city.prices[y * city.width + 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);
//}
}