Created
May 8, 2022 04:26
-
-
Save robert-king/eb8ce0eee18787f833258596e0f9393f to your computer and use it in GitHub Desktop.
codejam 2021 round 2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Problem: https://codingcompetitions.withgoogle.com/codejam/round/0000000000435915/00000000007dc2de | |
// video: https://www.youtube.com/c/RobertKing/videos | |
// twitter: https://twitter.com/robertkingNZ | |
fn main() { | |
let (r, w) = (std::io::stdin(), std::io::stdout()); | |
let mut sc = IO::new(r.lock(), w.lock()); | |
let t: usize = sc.read(); | |
for case_num in 1..=t { | |
let r: usize = sc.read(); | |
let c: usize = sc.read(); | |
let f: i64 = sc.read(); | |
let s: i64 = sc.read(); | |
let char_to_bool = &|c| c == 'M'; | |
let g1 = sc.grid(r, char_to_bool); | |
let g2 = sc.grid(r, char_to_bool); | |
let start = 0; | |
let end = 1; | |
let mut solver = primal_dual::MinimumCostFlowSolver::new(r*c*2+2); | |
for i in 0..r { | |
for j in 0..c { | |
let node = 2 + j + i*c; | |
solver.add_edge(start, node, 1, 0); | |
solver.add_edge(node + r*c, end, 1, 0); | |
} | |
} | |
// flat_map | |
for i in 0..r { | |
for j in 0..c { | |
for i2 in 0..r { | |
for j2 in 0..c { | |
let mut flip_cost = 0; | |
if g1[i][j] != g2[i][j] { | |
flip_cost += f; | |
} | |
if g1[i2][j2] != g2[i2][j2] { | |
flip_cost += f; | |
} | |
let mut cost = flip_cost; | |
if g1[i][j] == g2[i2][j2] && g1[i2][j2] == g2[i][j] { | |
let d = (i as i64 - i2 as i64).abs() + (j as i64 - j2 as i64).abs(); | |
let swap_cost = d*s; | |
cost = cost.min(swap_cost) | |
} | |
let node1 = 2 + j + i*c; | |
let node2 = 2 + j2 + i2*c + r*c; | |
solver.add_edge(node1, node2, 1, cost); | |
} | |
} | |
} | |
} | |
let ans = solver.solve(start, end, (r*c) as i64).unwrap()/2; | |
sc.write( | |
format!("Case #{}: {}", case_num, ans) | |
); | |
sc.write("\n"); | |
} | |
} | |
pub struct IO<R, W: std::io::Write>(R, std::io::BufWriter<W>); | |
impl<R: std::io::Read, W: std::io::Write> IO<R, W> { | |
pub fn new(r: R, w: W) -> IO<R, W> { | |
IO(r, std::io::BufWriter::new(w)) | |
} | |
pub fn write<S: ToString>(&mut self, s: S) { | |
use std::io::Write; | |
self.1.write_all(s.to_string().as_bytes()).unwrap(); | |
} | |
pub fn read<T: std::str::FromStr>(&mut self) -> T { | |
use std::io::Read; | |
let buf = self | |
.0 | |
.by_ref() | |
.bytes() | |
.map(|b| b.unwrap()) | |
.skip_while(|&b| b == b' ' || b == b'\n' || b == b'\r' || b == b'\t') | |
.take_while(|&b| b != b' ' && b != b'\n' && b != b'\r' && b != b'\t') | |
.collect::<Vec<_>>(); | |
unsafe { std::str::from_utf8_unchecked(&buf) } | |
.parse() | |
.ok() | |
.expect("Parse error.") | |
} | |
pub fn usize0(&mut self) -> usize { | |
self.read::<usize>() - 1 | |
} | |
pub fn vec<T: std::str::FromStr>(&mut self, n: usize) -> Vec<T> { | |
(0..n).map(|_| self.read()).collect() | |
} | |
pub fn chars(&mut self) -> Vec<char> { | |
self.read::<String>().chars().collect() | |
} | |
pub fn binary_vec(&mut self) -> Vec<u8> { | |
self.read::<String>() | |
.bytes() | |
.map(|b| b - b'0') | |
.collect() | |
} | |
pub fn grid<T, F>(&mut self, r: usize, f: &F) -> Vec<Vec<T>> | |
where F: Fn(char) -> T { | |
let mut g = Vec::with_capacity(r); | |
for _ in 0..r { | |
g.push( | |
self.chars().into_iter().map(f).collect() | |
) | |
} | |
g | |
} | |
} | |
pub mod primal_dual { | |
use std::cmp; | |
use std::collections::BinaryHeap; | |
type Flow = i64; | |
type Cost = i64; | |
const INF: Cost = 1 << 60; | |
struct Edge { | |
to: usize, | |
capacity: Flow, | |
flow: Flow, | |
cost: Cost, | |
reverse_to: usize, | |
} | |
impl Edge { | |
fn residue(&self) -> Flow { | |
self.capacity - self.flow | |
} | |
} | |
pub struct MinimumCostFlowSolver { | |
graph: Vec<Vec<Edge>>, | |
previous_edge: Vec<(usize, usize)>, | |
} | |
impl MinimumCostFlowSolver { | |
pub fn new(n: usize) -> Self { | |
MinimumCostFlowSolver { | |
graph: (0..n).map(|_| Vec::new()).collect(), | |
previous_edge: vec![(0, 0); n], | |
} | |
} | |
pub fn add_edge(&mut self, from: usize, to: usize, capacity: Flow, cost: Cost) { | |
let reverse_from = self.graph[to].len(); | |
let reverse_to = self.graph[from].len(); | |
self.graph[from].push(Edge { | |
to, | |
capacity, | |
flow: 0, | |
cost, | |
reverse_to: reverse_from, | |
}); | |
self.graph[to].push(Edge { | |
to: from, | |
capacity, | |
flow: capacity, | |
cost: -cost, | |
reverse_to, | |
}); | |
} | |
/// Find the minimum cost to send `flow` through a flow network from `source` to `sink`. | |
pub fn solve(&mut self, source: usize, sink: usize, mut flow: Flow) -> Option<Flow> { | |
let n = self.graph.len(); | |
let mut result = 0; | |
let mut h = vec![0; n]; | |
let mut q: BinaryHeap<(Cost, usize)> = BinaryHeap::new(); | |
while flow > 0 { | |
let mut dist = vec![INF; n]; | |
dist[source] = 0; | |
q.push((0, source)); | |
while let Some((current_dist, v)) = q.pop() { | |
if dist[v] < current_dist { | |
continue; | |
} | |
for (i, e) in self.graph[v].iter().enumerate() { | |
if e.residue() == 0 { | |
continue; | |
} | |
if dist[e.to] + h[e.to] > current_dist + h[v] + e.cost { | |
dist[e.to] = current_dist + h[v] + e.cost - h[e.to]; | |
self.previous_edge[e.to] = (v, i); | |
q.push((dist[e.to], e.to)); | |
} | |
} | |
} | |
if dist[sink] == INF { | |
return None; | |
} | |
for i in 0..n { | |
h[i] += dist[i]; | |
} | |
let mut df = flow; | |
let mut v = sink; | |
while v != source { | |
let (prev_v, prev_e) = self.previous_edge[v]; | |
df = cmp::min(df, self.graph[prev_v][prev_e].residue()); | |
v = prev_v; | |
} | |
flow -= df; | |
result += df * h[sink]; | |
let mut v = sink; | |
while v != source { | |
let (prev_v, prev_e) = self.previous_edge[v]; | |
self.graph[prev_v][prev_e].flow += df; | |
let reversed_edge_id = self.graph[prev_v][prev_e].reverse_to; | |
self.graph[v][reversed_edge_id].flow -= df; | |
v = prev_v; | |
} | |
} | |
Some(result) | |
} | |
/// Find the minimum cost to send `flow` through a flow network, which contains edges of | |
/// negative cost, from `source` to `sink`. | |
pub fn neg_solve(&mut self, source: usize, sink: usize, mut flow: Flow) -> Option<Flow> { | |
let n = self.graph.len(); | |
let mut result = 0; | |
while flow > 0 { | |
let mut dist = vec![INF; n]; | |
dist[source] = 0; | |
loop { | |
let mut updated = false; | |
for v in 0..n { | |
if dist[v] == INF { | |
continue; | |
} | |
for (i, e) in self.graph[v].iter().enumerate() { | |
if e.residue() == 0 { | |
continue; | |
} | |
if dist[e.to] > dist[v] + e.cost { | |
dist[e.to] = dist[v] + e.cost; | |
self.previous_edge[e.to] = (v, i); | |
updated = true; | |
} | |
} | |
} | |
if !updated { | |
break; | |
} | |
} | |
if dist[sink] == INF { | |
return None; | |
} | |
let mut df = flow; | |
let mut v = sink; | |
while v != source { | |
let (prev_v, prev_e) = self.previous_edge[v]; | |
df = cmp::min(df, self.graph[prev_v][prev_e].residue()); | |
v = prev_v; | |
} | |
flow -= df; | |
result += df * dist[sink]; | |
let mut v = sink; | |
while v != source { | |
let (prev_v, prev_e) = self.previous_edge[v]; | |
self.graph[prev_v][prev_e].flow += df; | |
let reversed_edge_id = self.graph[prev_v][prev_e].reverse_to; | |
self.graph[v][reversed_edge_id].flow -= df; | |
v = prev_v; | |
} | |
} | |
Some(result) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment