ãã®èšäºã¯ Competitive Programming (1) Advent Calendar 2019  3æ¥ç®ã®èšäºã§ãã ãã©ã«ã·ã¢æ ªåŒäŒç€Ÿã§ãšã³ãžãã¢ãããŠããæŸæ¬ã§ããæ¥åã§ã¯Rustã§ã€ã³ã¡ã¢ãªããŒã¿ããŒã¹ãéçºããŠããŸãã æ¥åžžç掻ã«ãããŠããæççµè·¯ãæ±ãããããšã¯ãããããšæããŸããäŸãã°æ
å
ã§ããã«ãŸã§ã®è¡ãæ¹ã調ã¹ãŠãããšãããã«ããªã³ã°ã§ãªãã¶ããããŠãããšããç¥ãåããäœäººæãã°ãã©ã³ã倧統é ã«ã€ãªãããèããŠãããšãããã³ã ãŒã«ããªãŒãºãã©ã€ãéºäŒãããããšããããããããªãã¯æççµè·¯ã«ã€ããŠèããŠããŸãã äŸç€ºããæ§é ã¯é ç¹ãšèŸºãããªãã°ã©ããšããŠè¡šçŸããããšãã§ããã°ã©ãäžã®ããç¹ããããç¹ãžã®æãçãçµè·¯ãèŠã€ããåé¡ãæççµè·¯åé¡ãšèšããŸãããã®èšäºã§ã¯ãRustã§æççµè·¯åé¡ãè§£ãã¢ã«ãŽãªãºã ã§ãããã€ã¯ã¹ãã©æ³ãšã¯ãŒã·ã£ã«ããã€ãæ³ãå®è£
ããŸããæççµè·¯é·ã ããæ±ããã®ã§ã¯ãªããçµè·¯åŸ©å
ãšåŒã°ããæççµè·¯ã®åºåãå®è£
ããŸãã åäžå§ç¹ããã®æççµè·¯ ãŸãã¯åäžå§ç¹ããã®æççµè·¯ãæ±ããŸããããè² ã®è·é¢ãæã£ã蟺ãç¡ãå Žåã ãã€ã¯ã¹ãã©æ³(Dijkstra's algorithm) ãšããã¢ã«ãŽãªãºã ãç¥ãããŠããŸããããã¯ãæ¢ã«æççµè·¯ã§å°éããé ç¹çŸ€(A)ã®ã©ãããšé£æ¥ããŠããé ç¹ã®ãã¡ãæãå§ç¹ããã®è·é¢ãçãé ç¹ãAã«è¿œå ããŠããã¢ã«ãŽãªãºã ã§ãã ãªããšRustã®å
¬åŒããã¥ã¡ã³ãã§ std::collections::binary_heapã®ãµã³ãã« ãšããŠãã€ã¯ã¹ãã©æ³ã®å®è£
ãäŸç€ºãããŠããŸã(ã¢ã«ãŽãªãºã ã®æ¬äœéšåãæç²ãããã®ãäžèš)ã // Dijkstra's shortest path algorithm. // Start at `start` and use `dist` to track the current shortest distance // to each node. This implementation isn't memory-efficient as it may leave duplicate // nodes in the queue. It also uses `usize::MAX` as a sentinel value, // for a simpler implementation. fn shortest_path(adj_list: &Vec<Vec<Edge>>, start: usize, goal: usize) -> Option<usize> { // dist[node] = current shortest distance from `start` to `node` let mut dist: Vec = (0..adj_list.len()).map(|_| usize::MAX).collect(); let mut heap = BinaryHeap::new(); // We're at `start`, with a zero cost dist[start] = 0; heap.push(State { cost: 0, position: start }); // Examine the frontier with lower cost nodes first (min-heap) while let Some(State { cost, position }) = heap.pop() { // Alternatively we could have continued to find all shortest paths if position == goal { return Some(cost); } // Important as we may have already found a better way if cost > dist[position] { continue; } // For each node we can reach, see if we can find a way with // a lower cost going through this node for edge in &adj_list[position] { let next = State { cost: cost + edge.cost, position: edge.node }; // If so, add it to the frontier and continue if next.cost ããŒã¿ã®æã¡æ¹ ã°ã©ãã衚çŸããæ¹æ³ãšããŠãåé ç¹ã«ã€ããŠé£æ¥ãã(ãã蟺ã®äž¡ç«¯ã«ãªã£ãŠãã)é ç¹ãªã¹ã(ãããã¯èŸºã®æ
å ±)ãä¿æãã飿¥ãªã¹ã(adjacency list)ãšãåé ç¹å¯Ÿã«ã€ããŠå
±æãã蟺ãããã°ãã®èŸºã®æ
å ±ãèšé²ãã飿¥è¡å(adjacency matrix)ããããŸãããã€ã¯ã¹ãã©æ³ã¯é£æ¥ããŠããé ç¹ãå¹çããèŠã€ãããããã飿¥ããŠããé ç¹ãªã¹ããä¿æãã飿¥ãªã¹ãã䜿ãã»ããå¹çãè¯ãã§ãã äŸãšããŠãã®ãããªæåã°ã©ãã飿¥ãªã¹ãã§è¡šçŸãããšäžèšã®ããã«è¡šãããšãã§ããŸãã let graph = vec![ vec![Edge { node: 2, cost: 10 }, Edge { node: 1, cost: 1 }], vec![Edge { node: 3, cost: 2 }], vec![ Edge { node: 1, cost: 1 }, Edge { node: 3, cost: 3 }, Edge { node: 4, cost: 1 }, ], vec![Edge { node: 0, cost: 7 }, Edge { node: 4, cost: 2 }], vec![], ]; çµè·¯åŸ©å
ããŠãäžè¿°ã®å®è£
ã§ã¯æççµè·¯é·ã ããè¿åŽãããå
·äœçãªçµè·¯ã¯æããã§ã¯ãããŸããã§ãããå
·äœçãªçµè·¯ãæ±ããæ¹æ³ãèããŸãããã åºåãããçµè·¯æ
å ± path = [start, ..., goal] ãšstartããåé ç¹ãžã®æççµè·¯é·æ
å ± dist ã«ã€ããŠèããŸãã d= dist[path[i]] ã§ãããšã path[i-1] 㯠d' = dist[path[i-1]] ãã€é ç¹i-1ããé ç¹iãžè·é¢d-d'ã®èŸºãæã£ãŠããã¯ãã§ãããã®ãããªé ç¹ãæ¢ãããšã§æççµè·¯ãæççµè·¯é·ã®æ
å ±ãã埩å
ããããšãã§ããŸãããã®æ¹æ³ãçŽ çŽã«å®è£
ãããšVãé ç¹æ°ãLãçµè·¯é·ãšããŠææªèšç®éO(V^2ã»L)ã«ãªã£ãŠããŸããŸãã æççµè·¯ãæ±ããéã«ïŒã€åã®é ç¹æ
å ±ãèšé²ããããšã§O(L)ã§åŸ©å
ããããšãã§ããŸããäžèšã®å®è£
ã§ã¯ pre_node ã«èšé²ããªããæççµè·¯é·ãæŽæ°ããŠããŸãã use std::cmp::Ordering; use std::collections::BinaryHeap; use std::usize; // BinaryHeapã«æž¡ãStateã®å®è£
#[derive(Copy, Clone, Eq, PartialEq)] struct State { cost: usize, position: usize, pre_node: usize, } impl Ord for State { fn cmp(&self, other: &State) -> Ordering { other .cost .cmp(&self.cost) .then_with(|| self.position.cmp(&other.position)) } } impl PartialOrd for State { fn partial_cmp(&self, other: &State) -> Option<Ordering> { Some(self.cmp(other)) } } struct Edge { node: usize, cost: usize, } fn shortest_path( adj_list: &Vec<Vec<Edge>>, start: usize, goal: usize, ) -> Option { let mut dist: Vec = (0..adj_list.len()).map(|_| usize::MAX).collect(); let mut pre_nodes: Vec = (0..adj_list.len()).map(|i| i).collect(); let mut heap = BinaryHeap::new(); dist[start] = 0; heap.push(State { cost: 0, position: start, pre_node: 0, }); while let Some(State { cost, position, pre_node, }) = heap.pop() { if cost > dist[position] { continue; } pre_nodes[position] = pre_node; if position == goal { let mut v = goal; let mut path = vec![goal]; while v != start { path.push(pre_nodes[v]); v = pre_nodes[v]; } path.reverse(); return Some((cost, path)); } for edge in &adj_list[position] { let next = State { cost: cost + edge.cost, position: edge.node, pre_node: position, }; if next.cost å
·äœçãªã°ã©ããšå§ç¹ã»çµç¹ãäžããçµæãæ£ããããšã確èªããŸãããã fn main() { let graph = vec![ vec![Edge { node: 2, cost: 10 }, Edge { node: 1, cost: 1 }], vec![Edge { node: 3, cost: 2 }], vec![ Edge { node: 1, cost: 1 }, Edge { node: 3, cost: 3 }, Edge { node: 4, cost: 1 }, ], vec![Edge { node: 0, cost: 7 }, Edge { node: 4, cost: 2 }], vec![], ]; // fn shortest_path( // adj_list: &Vec<Vec<Edge>>, // start: usize, // goal: usize, // ) -> Option assert_eq!(shortest_path(&graph, 0, 1), Some((1, vec![0, 1]))); assert_eq!(shortest_path(&graph, 0, 3), Some((3, vec![0, 1, 3]))); assert_eq!(shortest_path(&graph, 3, 0), Some((7, vec![3, 0]))); assert_eq!(shortest_path(&graph, 0, 4), Some((5, vec![0, 1, 3, 4]))); assert_eq!(shortest_path(&graph, 4, 0), None); } å
šç¹å¯Ÿéã®æççµè·¯ ç¶ããŠãå
šç¹å¯Ÿéã®æççµè·¯ãæ±ãã ã¯ãŒã·ã£ã«ããã€ãæ³(Warshall-Floyd Algorithm) ã«ã€ããŠãå®è£
ããŠãããŸãããã ããŒã¿ã®æã¡æ¹ åé ç¹å¯Ÿã«ã€ããŠèŸºãæã£ãŠããããé«éã«èª¿ã¹ããããã飿¥è¡åã䜿çšããŸãã飿¥ãªã¹ãã§è¡šçŸããã®ãšåãã°ã©ãã¯é£æ¥è¡åã§ã¯äžèšã®ããã«è¡šããŸãã let mut adj_matrix = vec![ vec![None, Some(1), Some(10), None, None], vec![None, None, None, Some(2), None], vec![None, Some(1), None, Some(3), Some(1)], vec![Some(7), None, None, None, Some(2)], vec![None, None, None, None, None], ]; â» None ã¯èŸºããªãããšã Some(x) ã¯è·é¢xã®èŸºãããããšãšå¯Ÿå¿ããŠããŸãã çµè·¯åŸ©å
ãã€ã¯ã¹ãã©æ³ãšåæ§ã«ïŒã€åã®æ
å ±ãä¿æããŠãããŠãçµç¹ããå§ç¹ãžåŸãåãã«é¡ã£ãŠãè¯ãã®ã§ããã next[i][j] =ãé ç¹iããé ç¹jã«è¡ããšããé ç¹iã®æ¬¡ã«éãé ç¹ã ãšããããã«æŽæ°ããŠããããšã§ãçŽæçã«åãããã©ãããšãã§ããããã«ãªããŸãã å®è£
ã瀺ããŸããäžè¿°ã® adj_matrix ã dist ãšããŠäžèšã® warshall_floyd 颿°ã«æž¡ããŸãã fn warshall_floyd(dist: &mut Vec<Vec<Option<usize>>>) -> Vec<Vec<usize>> { let n = dist.len(); let mut next = vec![]; // next[i][j] = j ã§åæå for i in 0..n { next.push((0..n).map(|j| j).collect::<Vec<usize>>()); } for k in 0..n { for i in 0..n { for j in 0..n { if let (Some(dik), Some(dkj)) = (dist[i][k], dist[k][j]) { if dist[i][j].is_none() || dist[i][j].unwrap() > dik + dkj { dist[i][j] = Some(dik + dkj); next[i][j] = next[i][k]; } } } } } next } 飿¥è¡åãæççµè·¯ã®è¡åãšèŠãŠç Žå£çã«æŽæ°ããªãããæ¬¡ã®é ç¹ãèšé²ããŸãã æŽæ°ãããæççµè·¯ã®è¡åãšæ¬¡ã®é ç¹ãèšé²ããè¡åãå§ç¹ããçµç¹ã«ãã©ãããšã§çµè·¯åŸ©å
ãå¯èœã§ãã fn shortest_path( dist: &Vec<Vec<Option<usize>>>, next: &Vec<Vec<usize>>, start: usize, goal: usize, ) -> Option { if dist[start][goal].is_none() { return None; } let mut path = vec![start]; let mut node = start; while node != goal { path.push(next[node][goal]); node = next[node][goal]; } Some((dist[start][goal].unwrap(), path)) } ãã¡ããåæ§ã«åäœã確èªã§ããŸãã fn main() { let mut dist = vec![ vec![None, Some(1), Some(10), None, None], vec![None, None, None, Some(2), None], vec![None, Some(1), None, Some(3), Some(1)], vec![Some(7), None, None, None, Some(2)], vec![None, None, None, None, None], ]; let next = warshall_floyd(&mut dist); assert_eq!(shortest_path(&dist, &next, 0, 1), Some((1, vec![0, 1]))); assert_eq!(shortest_path(&dist, &next, 0, 3), Some((3, vec![0, 1, 3]))); assert_eq!(shortest_path(&dist, &next, 3, 0), Some((7, vec![3, 0]))); assert_eq!(shortest_path(&dist, &next, 0, 4), Some((5, vec![0, 1, 3, 4]))); assert_eq!(shortest_path(&dist, &next, 4, 0), None); } ãŸãšã æççµè·¯åé¡ã«ã€ããŠå
·äœçãªçµè·¯ãæ±ããæ¹æ³ã®è§£èª¬ãšRustã§ã®å®è£
äŸã瀺ããŸããã ç§ã¯ãŸã å®åã§æççµè·¯åé¡ãè§£ããããšã¯ãããŸããããã°ã©ãçè«ã®å¥ã®åé¡ã§ããæå°è²»çšæµã®ã¢ã«ãŽãªãºã ã䜿ããå Žé¢ããããè²ã
ãªã¢ã«ãŽãªãºã ãç¥ã£ãŠããããšã¯å€§åã ãšèããŠããŸãã Googleã«ã¬ã³ããŒãšSlackããã®æ
å ±ã§ãã°ã«ãŒãåããã·ã£ããã«ã©ã³ãã¯ãããŸããïœãã¯ãããžãŒç·šïœ çããã奜ããªèšèªã§ãããããªã¢ã«ãŽãªãºã ãå®è£
ããŠã¿ãŠãã ããã ãŸãããã©ã«ã·ã¢ã«ã¯ç«¶æããã°ã©ãã³ã°éšããããå
æ¥è¡ããã PG BATTLE 2019 ã§ã¯äŒæ¥ã®éš248ããŒã äž9äœã®æçžŸãåããŸããããã©ã«ã·ã¢ã§åãããšã«èå³ãããæ¹ã¯ AtCoderJobs ãããã²ãé£çµ¡ãã ããïŒ