#![allow(unused_imports)]
use std::cmp::*;
use std::collections::*;
use std::io::Write;
use std::ops::Bound::*;

#[allow(unused_macros)]
macro_rules! debug {
    ($($e:expr),*) => {
        #[cfg(debug_assertions)]
        $({
            let (e, mut err) = (stringify!($e), std::io::stderr());
            writeln!(err, "{} = {:?}", e, $e).unwrap()
        })*
    };
}

fn to_index(x: usize, y: usize, w: usize) -> usize {
    x + y * w
}

fn from_index(idx: usize, w: usize) -> (usize, usize) {
    (idx % w, idx / w)
}

fn main() {
    let v = read_vec::<usize>();
    let (h, w) = (v[0], v[1]);
    let mut grid = vec![vec![]; h];
    for i in 0..h {
        grid[i] = read::<String>().chars().collect::<Vec<_>>();
    }

    let mut flow = Flow::new(h * w + 2);
    let s = h * w;
    let t = h * w + 1;
    for y in 0..h {
        for x in 0..w {
            if grid[y][x] == '#' {
                continue;
            }
            let cur = to_index(x, y, w);
            let adjacents = get_adjacents(x, y, w, h);
            if (x + y) % 2 == 0 {
                flow.add_edge(s, cur, 1);
                for (adj_x, adj_y) in adjacents {
                    let adj = to_index(adj_x, adj_y, w);
                    if grid[adj_y][adj_x] == '#' {
                        continue;
                    }
                    flow.add_edge(cur, adj, 1);
                }
            } else {
                flow.add_edge(cur, t, 1);
            }
        }
    }
    println!("{}", flow.max_flow_dinic(s, t));
    for y in 0..h {
        for x in 0..w {
            if grid[y][x] == '#' {
                continue;
            }
            if (x + y) % 2 == 1 {
                continue;
            }
            let cur = to_index(x, y, w);
            for &(to, cap, _) in &flow.edges[cur] {
                if cap == 0 && to < h * w {
                    let (adj_x, adj_y) = from_index(to, w);
                    if x == adj_x {
                        grid[min(y, adj_y)][x] = 'v';
                        grid[max(y, adj_y)][x] = '^';
                    } else {
                        grid[y][min(x, adj_x)] = '>';
                        grid[y][max(x, adj_x)] = '<';
                    }
                }
            }
        }
    }
    for line in grid {
        for ch in line {
            print!("{}", ch);
        }
        println!("");
    }
}

fn read<T: std::str::FromStr>() -> T {
    let mut s = String::new();
    std::io::stdin().read_line(&mut s).ok();
    s.trim().parse().ok().unwrap()
}

fn read_vec<T: std::str::FromStr>() -> Vec<T> {
    read::<String>()
        .split_whitespace()
        .map(|e| e.parse().ok().unwrap())
        .collect()
}

#[allow(dead_code)]
/// Struct for maximum flow problem
pub struct Flow {
    /// to, capacity, rev
    edges: Vec<Vec<(usize, usize, usize)>>,
}

impl Flow {
    #[allow(dead_code)]
    pub fn new(max_size: usize) -> Flow {
        Flow {
            edges: vec![Vec::new(); max_size + 1],
        }
    }

    #[allow(dead_code)]
    pub fn add_edge(&mut self, from: usize, to: usize, cap: usize) {
        let from_rev = self.edges[to].len();
        let to_rev = self.edges[from].len();

        self.edges[from].push((to, cap, from_rev));
        self.edges[to].push((from, 0, to_rev));
    }

    #[allow(dead_code)]
    /// Calculate maximum flow by dinic's algorithm
    pub fn max_flow_dinic(&mut self, s: usize, t: usize) -> usize {
        let mut flow = 0;
        loop {
            let level = self.bfs_dinic(s);
            let mut iter = vec![0; self.edges.len()];
            if level[t].is_none() {
                return flow;
            }

            loop {
                let f = self.dfs_dinic(s, t, usize::max_value(), &level, &mut iter);
                if f == 0 {
                    break;
                }
                flow += f;
            }
        }
    }

    #[allow(dead_code)]
    fn bfs_dinic(&self, s: usize) -> Vec<Option<usize>> {
        let mut level = vec![None; self.edges.len()];
        let mut que = VecDeque::new();

        level[s] = Some(0);
        que.push_back(s);

        while !que.is_empty() {
            let v = que.pop_front().unwrap();
            let l = Some(level[v].unwrap() + 1);

            for &(to, cap, _) in &self.edges[v] {
                if cap > 0 && level[to].is_none() {
                    level[to] = l;
                    que.push_back(to);
                }
            }
        }
        level
    }

    #[allow(dead_code)]
    fn dfs_dinic(
        &mut self,
        v: usize,
        t: usize,
        f: usize,
        level: &[Option<usize>],
        iter: &mut [usize],
    ) -> usize {
        if v == t {
            return f;
        }

        for i in iter[v]..self.edges[v].len() {
            iter[v] = i;

            let (to, cap, rev) = self.edges[v][i];
            if cap > 0 && level[v].unwrap() < level[to].unwrap_or(0) {
                let d = self.dfs_dinic(to, t, min(f, cap), level, iter);
                if d > 0 {
                    self.edges[v][i].1 -= d;
                    self.edges[to][rev].1 += d;
                    return d;
                }
            }
        }
        0
    }

    #[allow(dead_code)]
    fn max_flow_ff(&mut self, s: usize, t: usize) -> usize {
        let mut flow = 0;
        let l = self.edges.len();
        loop {
            let f = self.dfs_ff(s, t, usize::max_value(), &mut vec![false; l]);
            if f == 0 {
                break;
            }
            flow += f;
        }
        flow
    }

    #[allow(dead_code)]
    fn dfs_ff(&mut self, v: usize, t: usize, f: usize, used: &mut [bool]) -> usize {
        if v == t {
            return f;
        }
        used[v] = true;

        for i in 0..self.edges[v].len() {
            let (to, cap, rev) = self.edges[v][i];
            if !used[to] && cap > 0 {
                let d = self.dfs_ff(to, t, min(f, cap), used);
                if d > 0 {
                    self.edges[v][i].1 -= d;
                    self.edges[to][rev].1 += d;
                    return d;
                }
            }
        }
        0
    }

    #[allow(dead_code)]
    fn cut(&self, s: usize) -> BTreeSet<usize> {
        let mut stack = Vec::new();
        let mut ss = BTreeSet::new();
        ss.insert(s);
        stack.push(s);

        while !stack.is_empty() {
            let v = stack.pop().unwrap();
            for &(to, cap, _) in &self.edges[v] {
                if cap > 0 && !ss.contains(&to) {
                    ss.insert(to);
                    stack.push(to);
                }
            }
        }

        ss
    }
}

fn get_adjacents(x: usize, y: usize, w: usize, h: usize) -> Vec<(usize, usize)> {
    let mut adjacents = vec![];
    if x > 0 {
        adjacents.push((x - 1, y));
    }
    if y > 0 {
        adjacents.push((x, y - 1));
    }
    if y < h - 1 {
        adjacents.push((x, y + 1));
    }
    if x < w - 1 {
        adjacents.push((x + 1, y));
    }
    adjacents
}


Measured memory_bytes: 4880