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

164 lines
5.4 KiB

use crate::city;
use crate::city::{City, House, SIZE, HOUSE_RANGE};
use itertools::Itertools;
use std::collections::VecDeque;
pub fn try_combine(city: &City, layout1: &Vec<House>, layout2: &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();
// Make sure that we include all houses initially
while let Some(house) = houses2_sorted.pop() {
right.add_house(house);
}
// x is the last left coordinate, x+1 is right
for x in 0..SIZE {
// Update the lines
while let Some(house) = houses1_sorted.last() {
if house.x == x {
left.add_house(*house);
houses1_sorted.pop();
} else {
break;
}
}
right.remove_houses(x);
// Check compatibility of lines
if x == 0 {
// Cannot check this due to limitations in the implementation of LeftLine,
// it wouldn't be very interesting anyway.
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);
}
}
}
fn is_compatible(city: &City, left: &LeftLine, right: &RightLine) -> bool {
for y in 0..SIZE {
let max_left_covered_x = left.get_max_covered_x(y);
let min_right_covered_x = right.get_min_covered_x(y);
// 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;
}
}
}
true
}
struct LeftLine {
covers: Vec<usize>
}
struct RightLine {
covers: Vec<usize>,
houses: VecDeque<House>
}
impl LeftLine {
pub fn new() -> Self {
// XXX: Careful, default of 0 includes covering first vertical line
let covers = vec![0; SIZE];
LeftLine {covers}
}
pub fn add_house(&mut self, house: House) {
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);
}
}
pub fn get_max_covered_x(&self, y: usize) -> usize {
self.covers[y]
}
}
impl RightLine {
pub fn new() -> Self {
let covers = vec![usize::MAX; SIZE];
let houses = VecDeque::new();
RightLine {covers, houses}
}
pub fn add_house(&mut self, house: House) {
// 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);
}
pub fn remove_houses(&mut self, x: usize) {
// Has to be called with x, x+1, x+2...
while let Some(house) = self.houses.front() {
if house.x == x {
let removed_house = self.houses.pop_front().unwrap();
let removed_rect = removed_house.range_rectangle();
// Remove the now-outdated distances around the removed house
for y in removed_rect.top..=removed_rect.bottom {
self.covers[y] = usize::MAX;
}
// Update distances around the removed house if the area of any houses
// 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 {
removed_rect.top..=house_rect.bottom
};
for y in y_intersection {
self.covers[y] = self.covers[y].min(house_rect.left);
}
}
} else {
break;
}
}
}
pub fn get_min_covered_x(&self, y: usize) -> usize {
self.covers[y].min(SIZE)
}
}