Ř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.
232 lines
7.5 KiB
232 lines
7.5 KiB
use rand::prelude::{StdRng, SliceRandom};
|
|
use rand::{SeedableRng, Rng, thread_rng};
|
|
|
|
pub const SIZE: usize = 16384;
|
|
|
|
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 }
|
|
}
|
|
}
|
|
|
|
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 reachable = vec![false; SIZE * SIZE];
|
|
let mut houses: Vec<House> = Vec::new();
|
|
let mut claimed_houses = 0;
|
|
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) && !reachable[y * SIZE + x] {
|
|
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 !reachable[index] {
|
|
reachable[index] = true;
|
|
if city.is_house_xy(x as usize, y as usize) {
|
|
claimed_houses += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
houses.push(house);
|
|
//eprintln!("{} houses", houses.len());
|
|
break;
|
|
}
|
|
}
|
|
|
|
let finished = claimed_houses == city.get_house_count();
|
|
|
|
if finished {
|
|
break;
|
|
}
|
|
}
|
|
|
|
let mut price = get_price(&city, &houses);
|
|
eprintln!("Finished random init, price: {}", price);
|
|
|
|
const AROUND_RANGE: i32 = 50;
|
|
const MAX_CANDIDATES: usize = 20;
|
|
const MAX_FAILED_ITERATIONS: usize = 50;
|
|
|
|
let mut failed_iterations = 0;
|
|
while failed_iterations < MAX_FAILED_ITERATIONS {
|
|
let house = &houses.choose(&mut rng).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 {
|
|
break;
|
|
}
|
|
|
|
eprint!("Found candidate {}...", i);
|
|
let mut new_houses: Vec<_> = houses.to_vec().into_iter().filter(|h| &h != house).collect();
|
|
new_houses.push(candidate);
|
|
// TODO: This is_valid check could be way more efficient
|
|
if let Some(new_price) = is_valid(&city, &new_houses) {
|
|
let price_diff = new_price as i64 - price as i64;
|
|
eprintln!(" candidate is valid, price diff: {}.", price_diff);
|
|
eprintln!("Improved price: {}", new_price);
|
|
price = new_price;
|
|
houses = new_houses;
|
|
failed_iterations = 0;
|
|
break;
|
|
} else {
|
|
eprintln!(" candidate is invalid.");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Successful iterations always break
|
|
failed_iterations += 1;
|
|
}
|
|
|
|
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(&houses);
|
|
println!();
|
|
} else {
|
|
eprintln!("Finished randomization, price: {}, printing", price);
|
|
println!("Price {}, seed {}", price, seed);
|
|
print_houses(&houses);
|
|
println!();
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn get_neighbors(city: &City, house: &House) -> Vec<House> {
|
|
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<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);
|
|
|
|
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)
|
|
}
|
|
|