Problem:
Score : 100 points

Problem Statement

You are given a grid of N rows and M columns. The square at the i-th row and j-th column will be denoted as (i,j).
Some of the squares contain an object. All the remaining squares are empty.
The state of the grid is represented by strings S_1,S_2,\cdots,S_N. The square (i,j) contains an object if S_{i,j}= # and is empty if S_{i,j}= ..

Consider placing 1 \times 2 tiles on the grid. Tiles can be placed vertically or horizontally to cover two adjacent empty squares.
Tiles must not stick out of the grid, and no two different tiles may intersect. Tiles cannot occupy the square with an object.

Calculate the maximum number of tiles that can be placed and any configulation that acheives the maximum.

Constraints

1 \leq N \leq 100

1 \leq M \leq 100

S_i is a string with length M consists of # and ..

Input

Input is given from Standard Input in the following format:

N M
S_1
S_2
\vdots
S_N

Output

On the first line, print the maximum number of tiles that can be placed.

On the next N lines, print a configulation that achieves the maximum.
Precisely, output the strings t_1,t_2,\cdots,t_N constructed by the following way.

t_i is initialized to S_i.

For each (i,j), if there is a tile that occupies (i,j) and (i+1,j), change t_{i,j}:=v, t_{i+1,j}:=^.

For each (i,j), if there is a tile that occupies (i,j) and (i,j+1), change t_{i,j}:=>, t_{i,j+1}:=<.

See samples for further information.

You may print any configulation that maximizes the number of tiles.

Sample Input 1

3 3
#..
..#
...

Sample Output 1

3
#><
vv#
^^.

The following output is also treated as a correct answer.

3
#><
v.#
^><

Solution in C# (.NET Core 3.1.201) (judge verdict: Accepted)

using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Threading;
using System.Runtime.CompilerServices;
using System.Text;
using System.Diagnostics;
using System.Numerics;
using static System.Console;
using static System.Convert;
using static System.Math;
using static Template;
using SC = Scanner;

public partial class Solver
{
    int N, M;
    public void Solve()
    {
        sc.Make(out N, out M);
        var G = sc.Grid(N);
        var f = new Dinic(N * M + 2);

        for (int i = 0; i < N; i++)
            for (int j = 0; j < M; j++)
            {
                if (G[i][j] == '#') continue;
                if ((i + j) % 2 == 1)
                {
                    f.AddEdge(Id(i, j), N * M + 1, 1);
                    continue;
                }
                f.AddEdge(N * M, Id(i, j), 1);
                for (int k = 0; k < gh.Length; k++)
                {
                    int h = gh[k] + i, w = gw[k] + j;
                    if (!Inside(h, w, N, M) || G[h][w] == '#') continue;
                    f.AddEdge(Id(i, j), Id(h, w), 1);

                }
            }
        var res = f.Execute(N * M, N * M + 1);
        for(int i=0;i<f.edges.Count;i+=2)
        {
            var e = f.GetEdge(i);
            if (e.flow == 0 || e.fr >= N * M || e.to >= N * M) continue;
            int h1 = e.fr / M, w1 = e.fr % M, h2 = e.to / M, w2 = e.to % M;
            if (h1 > h2 || w1 > w2)
            {
                swap(ref h1, ref h2); swap(ref w1, ref w2);
            }
            if (h1 != h2)
            {
                G[h1][w1] = 'v';
                G[h2][w2] = '^';
            }
            else
            {
                G[h1][w1] = '>';
                G[h2][w2] = '<';
            }
        }
        Console.WriteLine(res);
        Console.WriteLine(G.Select(v => new string(v)).Concat("\n"));
    }
    public int Id(int h, int w) => h * M + w;
    int[] gh = new[] { 0, 0, 1, -1 }, gw = new[] { 1, -1, 0, 0 };
    public static bool Inside(int h, int w, int H, int W)
        => 0 <= h && h < H && 0 <= w && w < W;
}

class Dinic
{
    public List<Edge> edges;//(index&1)==0:edge, otherwise:reverse edge
    List<int>[] G;
    int[] mincost, iter;
    public Dinic(int V)
    {
        iter = new int[V];
        mincost = new int[V];
        edges = new List<Edge>();
        G = Create(V, () => new List<int>());
    }
    public void AddEdge(int from, int to, int cap)
    {
        int edgeIndex = edges.Count;
        G[from].Add(edgeIndex);
        edges.Add(new Edge(from, to, cap, edgeIndex));
        G[to].Add(edgeIndex + 1);
        edges.Add(new Edge(to, from, 0, edgeIndex + 1));
    }
    bool ExistAugmentingPath(int s, int t)
    {
        for (int i = 0; i < mincost.Length; i++) mincost[i] = -1;
        var Q = new Queue<int>();
        mincost[s] = 0;
        Q.Enqueue(s);
        while (Q.Any() && mincost[t] == -1)
        {
            var p = Q.Dequeue();
            foreach (var eidx in G[p])
            {
                var e = edges[eidx];
                if (e.cap > 0 && mincost[e.to] == -1)
                {
                    mincost[e.to] = mincost[p] + 1;
                    Q.Enqueue(e.to);
                }
            }
        }
        return mincost[t] != -1;
    }
    int FindBottleneck(int idx, int t, int flow)
    {
        if (idx == t) return flow;
        for (; iter[idx] < G[idx].Count; iter[idx]++)
        {
            var eg = edges[G[idx][iter[idx]]];
            if (eg.cap > 0 && mincost[idx] < mincost[eg.to])
            {
                var d = FindBottleneck(eg.to, t, Min(flow, eg.cap));
                if (d > 0)
                {
                    edges[G[idx][iter[idx]]].cap -= d;
                    edges[eg.idx ^ 1].cap += d;
                    return d;
                }
            }
        }
        return 0;
    }
    public Edge GetEdge(int i) => new Edge { fr = edges[i].fr, to = edges[i].to, cap = edges[i].cap + edges[i ^ 1].cap, flow = edges[i ^ 1].cap };
    public void ChangeEdge(int i,int newCap,int newFlow)
    {
        edges[i].cap = newCap - newFlow;
        edges[i ^ 1].cap = newFlow;
    }
    public int Execute(int s, int t, int f = int.MaxValue)
    {
        int flow = 0;
        while (f > 0 && ExistAugmentingPath(s, t))
        {
            Array.Clear(iter, 0, iter.Length);
            int df = 0;
            while ((df = FindBottleneck(s, t, f)) > 0) { flow += df; f -= df; }
        }
        return flow;
    }
    public bool[] MinCut(int s)
    {
        var res = new bool[G.Length];
        var Q = new Queue<int>();
        Q.Enqueue(s);
        res[s] = true;
        while (Q.Any())
        {
            var p = Q.Dequeue();
            foreach (var eidx in G[p])
            {
                var edge = edges[eidx];
                if (!res[edge.to] && edge.cap != 0)
                {
                    res[edge.to] = true;
                    Q.Enqueue(edge.to);
                }
            }
        }
        return res;
    }
    public class Edge
    {
        public int fr, to, idx; public int cap, flow;
        public Edge() { }
        public Edge(int f, int t, int c, int i)
        {
            fr = f; to = t; cap = c; idx = i; 
        }
    }
}
#region Template
public partial class Solver
{
    public SC sc = new SC();
    static void Main(string[] args)
    {
        Console.SetOut(new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false });
        var sol = new Solver();
        int testcase = 1;
        //testcase = sol.sc.Int;
        //var th = new Thread(sol.Solve, 1 << 26);th.Start();th.Join();
        while (testcase-- > 0)
            sol.Solve();
        Console.Out.Flush();
    }
}
public static class Template
{
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool chmin<T>(ref T a, T b) where T : IComparable<T> { if (a.CompareTo(b) > 0) { a = b; return true; } return false; }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool chmax<T>(ref T a, T b) where T : IComparable<T> { if (a.CompareTo(b) < 0) { a = b; return true; } return false; }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void swap<T>(ref T a, ref T b) { var t = b; b = a; a = t; }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void swap<T>(this IList<T> A, int i, int j) { var t = A[i]; A[i] = A[j]; A[j] = t; }
    public static T[] Create<T>(int n, Func<T> f) { var rt = new T[n]; for (var i = 0; i < rt.Length; ++i) rt[i] = f(); return rt; }
    public static T[] Create<T>(int n, Func<int, T> f) { var rt = new T[n]; for (var i = 0; i < rt.Length; ++i) rt[i] = f(i); return rt; }
    public static void Out<T>(this IList<T> A, out T a) => a = A[0];
    public static void Out<T>(this IList<T> A, out T a, out T b) { a = A[0]; b = A[1]; }
    public static void Out<T>(this IList<T> A, out T a, out T b, out T c) { A.Out(out a, out b); c = A[2]; }
    public static void Out<T>(this IList<T> A, out T a, out T b, out T c, out T d) { A.Out(out a, out b, out c); d = A[3]; }
    public static string Concat<T>(this IEnumerable<T> A, string sp) => string.Join(sp, A);
    public static char ToChar(this int s, char begin = '0') => (char)(s + begin);
    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> A) => A.OrderBy(v => Guid.NewGuid());
    public static int CompareTo<T>(this T[] A, T[] B, Comparison<T> cmp = null) { cmp = cmp ?? Comparer<T>.Default.Compare; for (var i = 0; i < Min(A.Length, B.Length); i++) { int c = cmp(A[i], B[i]); if (c > 0) return 1; else if (c < 0) return -1; } if (A.Length == B.Length) return 0; if (A.Length > B.Length) return 1; else return -1; }
    public static string ToStr<T>(this T[][] A) => A.Select(a => a.Concat(" ")).Concat("\n");
    public static int ArgMax<T>(this IList<T> A, Comparison<T> cmp = null) { cmp = cmp ?? Comparer<T>.Default.Compare; T max = A[0]; int rt = 0; for (int i = 1; i < A.Count; i++) if (cmp(max, A[i]) < 0) { max = A[i]; rt = i; } return rt; }
    public static T PopBack<T>(this List<T> A) { var v = A[A.Count - 1]; A.RemoveAt(A.Count - 1); return v; }
    public static void Fail<T>(T s) { Console.WriteLine(s); Console.Out.Close(); Environment.Exit(0); }
}
public class Scanner
{
    public string Str => Console.ReadLine().Trim();
    public int Int => int.Parse(Str);
    public long Long => long.Parse(Str);
    public double Double => double.Parse(Str);
    public int[] ArrInt => Str.Split(' ').Select(int.Parse).ToArray();
    public long[] ArrLong => Str.Split(' ').Select(long.Parse).ToArray();
    public char[][] Grid(int n) => Create(n, () => Str.ToCharArray());
    public int[] ArrInt1D(int n) => Create(n, () => Int);
    public long[] ArrLong1D(int n) => Create(n, () => Long);
    public int[][] ArrInt2D(int n) => Create(n, () => ArrInt);
    public long[][] ArrLong2D(int n) => Create(n, () => ArrLong);
    private Queue<string> q = new Queue<string>();
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public T Next<T>() { if (q.Count == 0) foreach (var item in Str.Split(' ')) q.Enqueue(item); return (T)Convert.ChangeType(q.Dequeue(), typeof(T)); }
    public void Make<T1>(out T1 v1) => v1 = Next<T1>();
    public void Make<T1, T2>(out T1 v1, out T2 v2) { v1 = Next<T1>(); v2 = Next<T2>(); }
    public void Make<T1, T2, T3>(out T1 v1, out T2 v2, out T3 v3) { Make(out v1, out v2); v3 = Next<T3>(); }
    public void Make<T1, T2, T3, T4>(out T1 v1, out T2 v2, out T3 v3, out T4 v4) { Make(out v1, out v2, out v3); v4 = Next<T4>(); }
    public void Make<T1, T2, T3, T4, T5>(out T1 v1, out T2 v2, out T3 v3, out T4 v4, out T5 v5) { Make(out v1, out v2, out v3, out v4); v5 = Next<T5>(); }
    public void Make<T1, T2, T3, T4, T5, T6>(out T1 v1, out T2 v2, out T3 v3, out T4 v4, out T5 v5, out T6 v6) { Make(out v1, out v2, out v3, out v4, out v5); v6 = Next<T6>(); }
    public void Make<T1, T2, T3, T4, T5, T6, T7>(out T1 v1, out T2 v2, out T3 v3, out T4 v4, out T5 v5, out T6 v6, out T7 v7) { Make(out v1, out v2, out v3, out v4, out v5, out v6); v7 = Next<T7>(); }
    public (T1, T2) Make<T1, T2>() { Make(out T1 v1, out T2 v2); return (v1, v2); }
    public (T1, T2, T3) Make<T1, T2, T3>() { Make(out T1 v1, out T2 v2, out T3 v3); return (v1, v2, v3); }
    public (T1, T2, T3, T4) Make<T1, T2, T3, T4>() { Make(out T1 v1, out T2 v2, out T3 v3, out T4 v4); return (v1, v2, v3, v4); }
}

#endregion