Problem:
〜魔女からこの世界を守るためキュゥべえと共に戦う 5 人の魔法少女達の物語〜

問題 A

問題文

薔薇園の魔女 GERTRUD は幅 W メートル，高さ H メートルの長方形の庭でバラを育てている．庭は一辺 1 メートルの正方形で区切られており，W\times H のブロックに分けられている．各ブロックはバラが育てられているか育てられていないかのどちらかである．

ただ驚くべきことに，この庭で育てられているバラはブロックを越えて絡み合い 1 つの生命体となっている．この生命体は辺を共有しているブロック間を越えて絡み合うことができ，どこかある 1 点を共有していれば同じ生命体となる．

巴マミは庭の左下の角から直線に必殺技「ティロ・フィナーレ」で巨大な銃を撃ち，その弾丸が通る直線上でその生命体を分割した時，生命体が出来る限り多くの分割数に分断されるようにすることによりダメージを与えたい．ティロ・フィナーレは最大でこの生物を何分割できるか求めよ．

入力形式

入力は以下の形式で与えられる．

H\ W\\
f_{11} f_{12} ... f_{1W}\\
f_{21} f_{22} ... f_{2W}\\
...\\
f_{H1} f_{H2} ... f_{HW}\\

H は庭の高さ，W は庭の幅を表す．

f_{ij} は庭の各ブロックを表す文字であり，上から i 行目，左から j 列目のブロックの状態を表す．ブロックにバラが育てられている場合は f_{ij} は '#' となり，そうでない場合は '.' となる．

出力形式

生命体を直線で分割する時に，最大でいくつの部分に分割できるかを1行で出力せよ．

制約

1 ≤ H ≤ 600

1 ≤ W ≤ 600

f_{ij}は '#', '.'のいずれかである．

少なくとも 1 つはf_{ij} = '#' となるマス f_{ij} が存在する．

2 つのブロックが辺を共有するとき，それらは隣接していると呼ぶことにする．任意の 2 つの異なる '#' のブロック f_A, f_B に対して，f_A から隣接している '#' のブロックを辿っていって f_B に着くことができる．(すなわち，薔薇のブロックは連結である．)

入力中で 1 行目の行，H 行目の行，1 列目の列，または W 列目の列にあるブロックを盤面の端のブロックと呼ぶことにする．任意の '.' のブロック f_C に対して，f_C から隣接している '.' のブロックを辿っていって，ある '.' の盤面の端のブロックに着くことができる．(すなわち，入力に"空洞"のブロックは存在しない．)

入出力例

入力例 1

3 5
##..#
#..##
####.

出力例1

4

このとき以下のように 4 つの部分に分割することができる．

入力例 2

3 3
#.#
###
#.#

出力例 2

3

入力例 3

10 9
.........
.........
####.....
#..#.##..
#..#..#..
#..##.###
#..##...#
##..##.##
###.#..#.
###.####.

出力例 3

6

入力例 4

10 11
###########
#.#.#.#.#.#
#.#.#.#.#.#
#.#.#.#.#.#
#.#.#.#.#.#
#.#.#.#.#.#
#.#.#.#.#.#
#.#.#.#.#.#
#.#.#.#.#.#
#.#.#.#.#.#

出力例 4

7

入力例 5

25 38
...........#...............#..........
...........###..........####..........
...........#####.......####...........
............###############...........
............###############...........
............##############............
.............#############............
............###############...........
...........#################..........
.......#...#################...#......
.......##.##################..##......
........####################.##.......
..........####################........
.........#####..########..#####.......
.......######....#####....######......
......########...#####..########.#....
....#######..#...#####..#..########...
..#########.....#######......#######..
...#######......#######........###....
..####.........#########........###...
...............#########..............
..............##########..............
..............##########..............
...............########...............
...............########...............

出力例 5

8

Problem Setter: Flat35

Solution in Rust (judge verdict: Accepted)

extern crate core;

use std::fmt;
use std::cmp::{max, min, Ordering};
use std::collections::{BinaryHeap, BTreeMap};
use std::fmt::{Display, Formatter, Error};
macro_rules! read_line{
    () => {{
        let mut line = String::new();
        std::io::stdin().read_line(&mut line).ok();
        line
    }};
    (delimiter: ' ') => {
        read_line!().split_whitespace().map(|x|x.to_string()).collect::<Vec<_>>()
    };
    (delimiter: $p:expr) => {
        read_line!().split($p).map(|x|x.to_string()).collect::<Vec<_>>()
    };
    (' ') => {
        read_line!(delimiter: ' ')
    };
    ($delimiter:expr) => {
        read_line!(delimiter: $delimiter)
    };
    (' '; $ty:ty) => {
        read_line!().split_whitespace().map(|x|x.parse::<$ty>().ok().unwrap()).collect::<Vec<$ty>>()
    };
    ($delimiter:expr; $ty:ty) => {
        read_line!($delimiter).into_iter().map(|x|x.parse::<$ty>().ok().unwrap()).collect::<Vec<$ty>>()
    };
}
macro_rules! let_all {
    ($($n:ident:$t:ty),*) => {
        let line = read_line!(delimiter: ' ');
        let mut iter = line.iter();
        $(let $n:$t = iter.next().unwrap().parse().ok().unwrap();)*
    };
}
#[derive(Copy, Clone)]
struct Coordinate {
    x: i32, y: i32
}
impl Coordinate {
    fn new(x: i32, y: i32) -> Coordinate {
        Coordinate{x: x, y: y}
    }
}
#[derive(Copy, Clone)]
enum Corner {
    Out{coordinate: Coordinate}, In{coordinate: Coordinate},
}
impl Corner {
    fn in_corner(x: usize, y: usize) -> Corner {
        Corner::In{coordinate: Coordinate::new(x as i32, y as i32)}
    }
    fn out_corner(x: usize, y: usize) -> Corner {
        Corner::Out {coordinate: Coordinate::new(x as i32, y as i32)}
    }
}
impl Ord for Corner {
    fn cmp(&self, other: &Self) -> Ordering {
        match (self, other) {
            (&Corner::In {coordinate: i}, &Corner::Out{coordinate: o}) => {
                match (i.x * o.y).cmp(&(o.x * i.y)) {
                    Ordering::Equal => Ordering::Greater,
                    o => o,
                }
            }
            (&Corner::Out {coordinate: i}, &Corner::In {coordinate: o}) => {
                match (i.x * o.y).cmp(&(o.x * i.y)) {
                    Ordering::Equal => Ordering::Less,
                    o => o,
                }
            }
            (&Corner::Out {coordinate: i}, &Corner::Out {coordinate: o}) => {
                (i.x * o.y).cmp(&(o.x * i.y))
            }
            (&Corner::In{coordinate: i}, &Corner::In {coordinate: o}) => {
                (i.x * o.y).cmp(&(o.x * i.y))
            }
        }
    }
}
impl PartialOrd for Corner {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}
impl PartialEq for Corner {
    fn eq(&self, other: &Self) -> bool {
        self.cmp(other).eq(&Ordering::Equal)
    }
}
impl Eq for Corner {}
impl Display for Corner {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        match self {
            &Corner::In {coordinate: c} => write!(f, "In(x: {}, y: {})", c.x, c.y),
            &Corner::Out {coordinate: c} => write!(f, "Out(x: {}, y: {})", c.x, c.y),
        }
    }
}
fn main(){
    let_all!(h: usize, w: usize);
    let mut state = Vec::<Vec<char>>::with_capacity(h);
    for _ in 0 .. h{
        state.push(read_line!().chars().collect());
    }
    state.reverse();
    let state = state;
    let mut corners = Vec::new();
    for x in 0 .. w {
        for y in 0 .. h {
            match state[y][x] {
                '#' => {
                    if (x < 1 || state[y][x - 1] == '.') && (y + 1 >= h || state[y + 1][x] == '.') {
                        corners.push(Corner::in_corner(x, y + 1))
                    }
                    if (x + 1 >= w || state[y][x + 1] == '.') && (y < 1 || state[y - 1][x] == '.') {
                        corners.push(Corner::out_corner(x + 1, y))
                    }
                }
                '.' => {
                    if (x >= 1 && state[y][x - 1] == '#') && (y + 1 < h && state[y + 1][x] == '#') {
                        corners.push(Corner::in_corner(x, y + 1))
                    }
                    if (x + 1 < w && state[y][x + 1] == '#') && (y >= 1 && state[y - 1][x] == '#') {
                        corners.push(Corner::out_corner(x + 1, y))
                    }
                }
                _ => unreachable!()
            }
        }
    }
    corners.sort();
    let corners = corners;
    let mut max = 1;
    let mut count = 1;
    for &c in &corners {
        //println!("{}", c);
        match c {
            Corner::In {coordinate: _} => count += 1,
            Corner::Out {coordinate: _} => count -= 1,
        }
        if max < count {
            max = count;
        }
    }
    println!("{}", max);

}