2016/24
This commit is contained in:
249
2016/24/src/main.rs
Normal file
249
2016/24/src/main.rs
Normal file
@@ -0,0 +1,249 @@
|
||||
use std::io::{self, BufRead};
|
||||
use std::collections::{BTreeSet, BTreeMap, HashMap};
|
||||
use std::usize;
|
||||
|
||||
|
||||
pub type Cell = (usize, usize);
|
||||
|
||||
|
||||
pub trait PathBuilder {
|
||||
fn neighbors(&mut self, cell: Cell) -> Vec<Cell>;
|
||||
fn distance_between(&mut self, start: Cell, goal: Cell) -> usize;
|
||||
|
||||
fn heuristic_cost_estimate(&mut self, start: Cell, goal: Cell) -> usize {
|
||||
self.distance_between(start, goal)
|
||||
}
|
||||
|
||||
fn reconstruct_path(&self, came_from: &BTreeMap<Cell, Cell>, current: Cell) -> Vec<Cell> {
|
||||
let mut current = current;
|
||||
let mut path = Vec::new();
|
||||
|
||||
path.push(current);
|
||||
|
||||
while came_from.contains_key(¤t) {
|
||||
if let Some(next) = came_from.get(¤t) {
|
||||
current = *next;
|
||||
|
||||
path.push(*next);
|
||||
}
|
||||
}
|
||||
|
||||
path
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn path_between(builder: &mut PathBuilder, start: Cell, goal: Cell) -> Result<Vec<Cell>, ()> {
|
||||
let mut closed_set = BTreeSet::new();
|
||||
let mut open_set = BTreeSet::new();
|
||||
|
||||
open_set.insert(start);
|
||||
|
||||
let mut came_from = BTreeMap::new();;
|
||||
|
||||
let mut g_score = BTreeMap::new();
|
||||
|
||||
g_score.insert(start, 0);
|
||||
|
||||
let mut f_score = BTreeMap::new();
|
||||
|
||||
f_score.insert(start, builder.heuristic_cost_estimate(start, goal));
|
||||
|
||||
while !open_set.is_empty() {
|
||||
let (current, _) = f_score.iter()
|
||||
.filter(|&(&(x, y), _)| open_set.contains(&(x, y)))
|
||||
.fold(((0, 0), usize::MAX), |((min_x, min_y), min_score), (&(x, y), &score)| {
|
||||
if score < min_score {
|
||||
((x, y), score)
|
||||
} else {
|
||||
((min_x, min_y), min_score)
|
||||
}
|
||||
});
|
||||
|
||||
if current == goal {
|
||||
return Ok(builder.reconstruct_path(&came_from, current));
|
||||
}
|
||||
|
||||
open_set.remove(¤t);
|
||||
closed_set.insert(current);
|
||||
|
||||
for neighbor in builder.neighbors(current) {
|
||||
if closed_set.contains(&neighbor) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let tentative_g_score = *g_score
|
||||
.entry(current)
|
||||
.or_insert(usize::MAX) + builder.distance_between(current, neighbor);
|
||||
|
||||
if !open_set.contains(&neighbor) {
|
||||
open_set.insert(neighbor);
|
||||
} else if tentative_g_score >= *g_score.entry(neighbor).or_insert(usize::MAX) {
|
||||
continue
|
||||
}
|
||||
|
||||
came_from.insert(neighbor, current);
|
||||
|
||||
g_score.insert(neighbor, tentative_g_score);
|
||||
f_score.insert(neighbor, tentative_g_score + builder.heuristic_cost_estimate(neighbor, goal));
|
||||
}
|
||||
}
|
||||
|
||||
Err(())
|
||||
}
|
||||
|
||||
|
||||
pub struct Grid {
|
||||
grid: Vec<Vec<char>>
|
||||
}
|
||||
|
||||
impl PathBuilder for Grid {
|
||||
fn neighbors(&mut self, cell: Cell) -> Vec<Cell> {
|
||||
let mut neighbors = Vec::new();
|
||||
|
||||
// Check cell above.
|
||||
if cell.1 > 0 && self.grid[cell.1 - 1][cell.0] != '#' {
|
||||
neighbors.push((cell.0, cell.1 - 1));
|
||||
}
|
||||
|
||||
// Check cell below.
|
||||
if cell.1 < self.grid.len() - 1 && self.grid[cell.1 + 1][cell.0] != '#' {
|
||||
neighbors.push((cell.0, cell.1 + 1));
|
||||
}
|
||||
|
||||
// Check left cell.
|
||||
if cell.0 > 0 && self.grid[cell.1][cell.0 - 1] != '#' {
|
||||
neighbors.push((cell.0 - 1, cell.1));
|
||||
}
|
||||
|
||||
// Check right cell.
|
||||
if cell.0 < self.grid[0].len() - 1 && self.grid[cell.1][cell.0 + 1] != '#' {
|
||||
neighbors.push((cell.0 + 1, cell.1));
|
||||
}
|
||||
|
||||
neighbors
|
||||
}
|
||||
|
||||
fn distance_between(&mut self, start: Cell, goal: Cell) -> usize {
|
||||
let delta_x = goal.0 as i64 - start.0 as i64;
|
||||
let delta_y = goal.1 as i64 - start.1 as i64;
|
||||
|
||||
(delta_x.abs() + delta_y.abs()) as usize
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn main() {
|
||||
let stdin = io::stdin();
|
||||
|
||||
let grid = stdin.lock().lines()
|
||||
.filter_map(|line| line.ok())
|
||||
.map(|line| line.chars().collect::<Vec<_>>())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut grid = Grid { grid: grid };
|
||||
|
||||
let targets: HashMap<char, (usize, usize)> = grid.grid.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(y, row)| {
|
||||
row.iter()
|
||||
.cloned()
|
||||
.enumerate()
|
||||
.filter(|&(_, c)| c != '#' && c != '.')
|
||||
.map(move |(x, c)| (c, (x, y)))
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
||||
let mut table = HashMap::new();
|
||||
|
||||
for left in targets.iter() {
|
||||
for right in targets.iter().filter(|right| *right != left) {
|
||||
if let Ok(path) = path_between(&mut grid, *left.1, *right.1) {
|
||||
table.entry(left.0.clone())
|
||||
.or_insert(HashMap::new())
|
||||
.insert(right.0.clone(), path.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Part 1.
|
||||
let mut queue = Vec::new();
|
||||
|
||||
queue.push((0, vec!['0']));
|
||||
|
||||
'main: loop {
|
||||
let mut variants = Vec::new();
|
||||
|
||||
for &(ref total, ref items) in queue.iter() {
|
||||
if items.len() == targets.len() {
|
||||
break 'main;
|
||||
}
|
||||
|
||||
let last = items.last().unwrap();
|
||||
|
||||
for (next, distance) in table.get(last).unwrap().iter() {
|
||||
if items.contains(&next) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut variant = items.clone();
|
||||
|
||||
variant.push(next.clone());
|
||||
|
||||
variants.push((total + distance - 1, variant));
|
||||
}
|
||||
}
|
||||
|
||||
queue = variants;
|
||||
}
|
||||
|
||||
queue.sort_by_key(|&(distance, _)| distance);
|
||||
|
||||
println!("{:#?}", queue[0]);
|
||||
|
||||
// Part 2.
|
||||
let mut queue = Vec::new();
|
||||
|
||||
queue.push((0, vec!['0']));
|
||||
|
||||
'main2: loop {
|
||||
let mut variants = Vec::new();
|
||||
|
||||
for &(ref total, ref items) in queue.iter() {
|
||||
let last = items.last().unwrap();
|
||||
|
||||
if items.len() >= targets.len() {
|
||||
if *last != '0' {
|
||||
if let Some(distance) = table.get(last).unwrap().get(&'0') {
|
||||
let mut variant = items.clone();
|
||||
|
||||
variant.push('0');
|
||||
|
||||
variants.push((total + distance - 1, variant));
|
||||
}
|
||||
} else {
|
||||
break 'main2;
|
||||
}
|
||||
}
|
||||
|
||||
for (next, distance) in table.get(last).unwrap().iter() {
|
||||
if items.contains(&next) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut variant = items.clone();
|
||||
|
||||
variant.push(next.clone());
|
||||
|
||||
variants.push((total + distance - 1, variant));
|
||||
}
|
||||
}
|
||||
|
||||
queue = variants;
|
||||
}
|
||||
|
||||
queue.sort_by_key(|&(distance, _)| distance);
|
||||
|
||||
println!("{:#?}", queue[0]);
|
||||
}
|
||||
Reference in New Issue
Block a user