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.#
^><



Sample input:
3 3
#..
..#
...


Sample output:
3
#><
vv#
^^.

Submission status: Accepted

Solution code:
from collections import deque

class mf_graph:
    def __init__(self,N):
        self.N = N
        self.graph = [[] for _ in range(N)]
    
    def add_edge(self,v,w,cap):
        edge = [w,cap,None]
        edge[2] = rev_edge = [v,0,edge]
        self.graph[v].append(edge)
        self.graph[w].append(rev_edge)
    
    def bfs(self,s,t):
        self.level = [None]*self.N
        dq = deque([s])
        self.level[s] = 0
        while dq:
            v = dq.popleft()
            nlevel = self.level[v]+1
            for w,cap,_ in self.graph[v]:
                if cap and self.level[w] is None:
                    self.level[w] = nlevel
                    dq.append(w)
        return self.level[t] is not None
    
    def dfs(self,s,v,f):
        if v == s:
            return f
        for rev in self.it[v]:
            w,_,e = rev
            cap = e[1]
            if cap and self.level[w] is not None:
                if self.level[v] > self.level[w]:
                    d = self.dfs(s,w,min(f,cap))
                    if d:
                        e[1] -= d
                        rev[1] += d
                        return d
        self.level[v] == self.N
        return 0
    
    def flow(self,s,t,flow_limit=float('inf')):
        flow = 0
        while self.bfs(s,t):
            *self.it, = map(iter,self.graph)
            f = flow_limit
            while f:
                f = self.dfs(s,t,flow_limit-flow)
                flow += f
        return flow

N,M = map(int, input().split())
S = [list(input()) for i in range(N)]
dc = mf_graph(N*M+2)

s =N*M
t = N*M+1
for i in range(N):
    for j in range(M):
        if (i+j) % 2 == 0:
            dc.add_edge(s,M*i+j,1)
        else:
            dc.add_edge(M*i+j,t,1)

for i in range(N):
    for j in range(M):
        if j+1 < M and S[i][j] == "." and S[i][j+1] == ".":
            u,v = M*i+j,M*i+j+1
            if (i+j) % 2 == 1:
                u,v = v,u
            dc.add_edge(u,v,1)
        if i+1 < N and S[i][j] == "." and S[i+1][j] == ".":
            u,v = M*i+j, M*(i+1)+j
            if (i+j) % 2 == 1:
                u,v = v,u
            dc.add_edge(u,v,1)

print(dc.flow(s,t,10**9))
for u in range(N*M+2):
    for v,cap,_ in dc.graph[u]:
        ui,uj = divmod(u,M)
        vi,vj = divmod(v,M)
        if (ui+uj) % 2 == 0 and cap == 0 and u != s and u != t and v != s and v!= t:
            if ui+1 == vi:
                S[ui][uj] = "v"
                S[vi][vj] = "^"
            elif ui == vi+1:
                S[ui][uj] = "^"
                S[vi][vj] = "v"
            elif uj+1 == vj:
                S[ui][uj] = ">"
                S[vi][vj] = "<"
            elif uj == vj+1:
                S[ui][uj] = "<"
                S[vi][vj] = ">"

for res in S:
    print("".join(res))

Submission status: Accepted

Solution code:
N,M = map(int,input().split())
S = [input() for i in range(N)]

P = N*M+2
src, dst = N*M, N*M+1
es = [[] for i in range(P)] # [[to1,cap1,rev1], ...]
pos = []

def add_edge(fr,to,cap):
    pos.append((fr, len(es[fr])))
    es[fr].append([to,cap,len(es[to])])
    es[to].append([fr,0,len(es[fr])-1])

for i,row in enumerate(S):
    for j,c in enumerate(row):
        if c=='#': continue
        if (i+j)%2==0:
            add_edge(src, i*M+j, 1) #src -> even
        else:
            add_edge(i*M+j, dst, 1) #odd -> dst
#even -> odd
for i,row in enumerate(S):
    for j,c in enumerate(row):
        if c=='#' or (i+j)%2: continue
        v = i*M+j
        if i and S[i-1][j]=='.':
            add_edge(v, (i-1)*M+j, 1)
        if j and S[i][j-1]=='.':
            add_edge(v, i*M+j-1, 1)
        if i+1 < N and S[i+1][j]=='.':
            add_edge(v, (i+1)*M+j, 1)
        if j+1 < M and S[i][j+1]=='.':
            add_edge(v, i*M+j+1, 1)

INF = float('inf')
level = [0] * P
iters = [0] * P
from collections import deque
def dinic_max_flow(s,t):
    global iters

    def _bfs(s):
        global level
        level = [-1] * P
        level[s] = 0
        q = deque([s])
        while q:
            v = q.popleft()
            for to,cap,rev in es[v]:
                if cap > 0 and level[to] < 0:
                    level[to] = level[v] + 1
                    q.append(to)

    def _dfs(v,t,f):
        if v == t: return f
        for i in range(iters[v],len(es[v])):
            iters[v] += 1
            to,cap,rev = es[v][i]
            if es[v][i][1] > 0 and level[v] < level[to]:
                d = _dfs(to,t,min(f,es[v][i][1]))
                if d > 0:
                    es[v][i][1] -= d #cap
                    es[to][rev][1] += d
                    return d
        return 0

    flow = 0
    while True:
        _bfs(s)
        if level[t] < 0: return flow
        iters = [0] * P
        f = 0
        while True:
            f = _dfs(s,t,INF)
            if f <= 0: break
            flow += f

print(dinic_max_flow(src, dst))

ans = [list(row) for row in S]
for fr,i in pos:
    if fr==src: continue
    to,cap,rev = es[fr][i]
    if to==dst: continue
    flow = es[to][rev][1]
    if flow==0: continue
    i0,j0 = divmod(fr,M)
    i1,j1 = divmod(to,M)
    if i0 == i1+1:
        ans[i1][j1] = 'v'
        ans[i0][j0] = '^'
    elif j0 == j1+1:
        ans[i1][j1] = '>'
        ans[i0][j0] = '<'
    elif i0 == i1-1:
        ans[i0][j0] = 'v'
        ans[i1][j1] = '^'
    else:
        ans[i0][j0] = '>'
        ans[i1][j1] = '<'
for row in ans:
    print(''.join(row))

Submission status: Accepted

Solution code:
class Weigthed_Digraph:
    #入力定義
    def __init__(self,vertex=[]):
        self.vertex=set(vertex)

        self.edge_number=0
        self.vertex_number=len(vertex)

        self.adjacent_out={v:{} for v in vertex} #出近傍(vが始点)
        self.adjacent_in={v:{} for v in vertex} #入近傍(vが終点)

    #頂点の追加
    def add_vertex(self,*adder):
        for v in adder:
            if v not in self.vertex:
                self.adjacent_in[v]={}
                self.adjacent_out[v]={}

                self.vertex_number+=1
                self.vertex.add(v)

    #辺の追加(更新)
    def add_edge(self,From,To,weight=1):
        for v in [From,To]:
            if v not in self.vertex:
                self.add_vertex(v)

        if To not in self.adjacent_in[From]:
            self.edge_number+=1

        self.adjacent_out[From][To]=weight
        self.adjacent_in[To][From]=weight

    #辺を除く
    def remove_edge(self,From,To):
        for v in [From,To]:
            if v not in self.vertex:
                self.add_vertex(v)

        if To in self.adjacent_out[From]:
            del self.adjacent_out[From][To]
            del self.adjacent_in[To][From]
            self.edge_number-=1

    #頂点を除く
    def remove_vertex(self,*vertexes):
        for  v in vertexes:
            if v in self.vertex:
                self.vertex_number-=1

                for u in self.adjacent_out[v]:
                    del self.adjacent_in[u][v]
                    self.edge_number-=1
                del self.adjacent_out[v]

                for u in self.adjacent_in[v]:
                    del self.adjacent_out[u][v]
                    self.edge_number-=1
                del self.adjacent_in[v]

    #Walkの追加
    def add_walk(self,*walk):
        pass

    #Cycleの追加
    def add_cycle(self,*cycle):
        pass

    #頂点の交換
    def __vertex_swap(self,p,q):
        self.vertex.sort()

    #グラフに頂点が存在するか否か
    def vertex_exist(self,v):
        return v in self.vertex

    #グラフに辺が存在するか否か
    def edge_exist(self,From,To):
        if not(self.vertex_exist(From) and self.vertex_exist(To)):
            return False
        return To in self.adjacent_out[From]

    #近傍
    def neighbohood(self,v):
        if not self.vertex_exist(v):
            return []
        return list(self.adjacent[v])

    #出次数
    def out_degree(self,v):
        if not self.vertex_exist(v):
            return 0

        return len(self.adjacent_out[v])

    #入次数
    def in_degree(self,v):
        if not self.vertex_exist(v):
            return 0

        return len(self.adjacent_in[v])

    #次数
    def degree(self,v):
        if not self.vertex_exist(v):
            return 0

        return self.out_degree(v)-self.in_degree(v)

    #頂点数
    def vertex_count(self):
        return len(self.vertex)

    #辺数
    def edge_count(self):
        return self.edge_number

    #頂点vを含む連結成分
    def connected_component(self,v):
        pass

def Flow_by_Dinic(D,source,sink,Mode=False):
    from collections import deque
    from copy import copy

    """Source sからSink tへの最大流問題をDinic法で解く.\n

    Mode:
    True---各頂点の流し方も返す
    False---解答のみ
    (参考元:https://tjkendev.github.io/procon-library/python/max_flow/dinic.html)
    """
    def bfs(s,t):
        for v in D.vertex:
            Level[v]=None

        Q=deque([s])
        Level[s]=0

        while Q:
            v=Q.popleft()
            for w,cap,_ in H[v]:
                if cap and Level[w] is None:
                    Level[w]=Level[v]+1
                    Q.append(w)

        return Level[t] is not None

    def dfs(v,t,f):
        if v==t:
            return f

        for e in H[v]:
            w,cap,rev=e
            if cap and Level[v]<Level[w]:
                d=dfs(w,t,min(f,cap))

                if d:
                    e[1]-=d
                    rev[1]+=d
                    return d
        return 0

    s,t=source,sink
    inf=float("inf")
    flow=0
    Level={v:None for v in D.vertex}

    H={v:[] for v in D.vertex}

    for u in D.vertex:
        for v in D.adjacent_out[u]:
            F=[v,D.adjacent_out[u][v],None]
            F[2]=B=[u,0,F]
            H[u].append(F)
            H[v].append(B)

    while bfs(s,t):
        f=inf
        while f:
            f=dfs(s,t,inf)
            flow+=f

    if Mode:
        R={}
        for v in D.vertex:
            R[v]={}
            for S in H[v]:
                w=S[0]
                if w in D.adjacent_out[v]:
                    R[v][w]=S[-1][1]

        return flow,R
    else:
        return flow
#================================================
N,M=map(int,input().split())
D=Weigthed_Digraph([(y,x) for x in range(1,M+1) for y in range(1,N+1)])
D.add_vertex("s","t")

S=["*"*(M+2) for _ in range(N+2)]
for i in range(1,N+1):
    S[i]="*"+input()+"*"

H=[(1,0),(-1,0),(0,1),(0,-1)]
for y in range(1,N+1):
    for x in range(1,M+1):
        if S[y][x]!=".":
            continue

        if (x+y)%2:
            D.add_edge("s",(y,x),1)
            for (a,b) in H:
                if S[y+a][x+b]==".":
                    D.add_edge((y,x),(y+a,x+b),1)
        else:
            D.add_edge((y,x),"t",1)

flow,F=Flow_by_Dinic(D,"s","t",True)

U=[[S[j][i] for i in range(M+2)] for j in range(N+2)]
for y in range(1,N+1):
    for x in range(1,M+1):
        for C in F[(y,x)]:
            if isinstance(C,str):
                continue
            elif F[(y,x)][C]==0:
                continue

            q,p=C
            if (y+1==q) and (x==p):
                alpha,beta="v","^"
            elif (y-1==q) and (x==p):
                alpha,beta="^","v"
            elif (y==q) and (x+1==p):
                alpha,beta=">","<"
            elif (y==q) and (x-1==p):
                alpha,beta="<",">"

            U[y][x]=alpha
            U[q][p]=beta

print(flow)
Y=["".join(T[1:-1]) for T in U[1:-1]]
print("\n".join(Y))

Submission status: Accepted

Solution code:
import sys
from collections import *

sys.setrecursionlimit(10 ** 6)
int1 = lambda x: int(x) - 1
p2D = lambda x: print(*x, sep="\n")
def II(): return int(sys.stdin.readline())
def MI(): return map(int, sys.stdin.readline().split())
def MI1(): return map(int1, sys.stdin.readline().split())
def LI(): return list(map(int, sys.stdin.readline().split()))
def LI1(): return list(map(int1, sys.stdin.readline().split()))
def LLI(rows_number): return [LI() for _ in range(rows_number)]
def SI(): return sys.stdin.readline()[:-1]
dij = [(1, 0), (-1, 0), (0, 1), (0, -1)]

# __dfsのflow_to_meの初期値がinfに相当するか注意
# n頂点,sスタート,tゴール
class Dinic:
    def __init__(self, n, s, t):
        self.n, self.s, self.t = n, s, t
        self.to = [[] for _ in range(n)]
        self.max_flow = -1

    def add_edge(self, u, v, cap):
        u_index_in_to_v = len(self.to[v])
        v_index_in_to_u = len(self.to[u])
        self.to[u].append([v, cap, u_index_in_to_v])
        self.to[v].append([u, 0, v_index_in_to_u])

    # 無向辺の追加
    def add_undir_edge(self, u, v, cap):
        u_index_in_to_v = len(self.to[v])
        v_index_in_to_u = len(self.to[u])
        self.to[u].append([v, cap, u_index_in_to_v])
        self.to[v].append([u, cap, v_index_in_to_u])

    def __set_level(self):
        s = self.s
        level = [-1] * self.n
        level[s] = 0
        q = deque()
        q.append([s, 0])
        while q:
            u, u_level = q.popleft()
            for v, cap, _ in self.to[u]:
                if cap == 0: continue
                if level[v] != -1: continue
                level[v] = u_level + 1
                if v == self.t:
                    self.level = level
                    return True
                q.append([v, u_level + 1])
        return False

    def __dfs(self, u=-1, flow_to_me=10 ** 16):
        if u == -1: u = self.s
        if u == self.t: return flow_to_me
        flow_from_me = 0
        u_level = self.level[u]
        for utov_i, (v, cap, vtou_i) in enumerate(self.to[u]):
            if self.level[v] != u_level + 1: continue
            if cap == 0: continue
            flow_to_v = self.__dfs(v, min(cap, flow_to_me - flow_from_me))
            if not flow_to_v: continue
            flow_from_me += flow_to_v
            self.to[u][utov_i][1] -= flow_to_v
            self.to[v][vtou_i][1] += flow_to_v
        return flow_from_me

    def __calculation(self):
        res = 0
        while self.__set_level():
            res += self.__dfs()
        return res

    # これが出力用
    def get_max_flow(self):
        if self.max_flow == -1:
            self.max_flow = self.__calculation()
        return self.max_flow

h,w=MI()
ss=[list(SI()) for _ in range(h)]
mf=Dinic(h*w+2,h*w,h*w+1)
for i in range(h):
    for j in range(w):
        if ss[i][j]=="#":continue
        if (i+j)%2:
            mf.add_edge(i*w+j,h*w+1,1)
        else:
            mf.add_edge(h*w,i*w+j,1)
            for di,dj in dij:
                ni,nj=i+di,j+dj
                if ni<0 or nj<0 or ni>=h or nj>=w:continue
                if ss[ni][nj]=="#":continue
                mf.add_edge(i*w+j,ni*w+nj,1)

ans=mf.get_max_flow()

for i in range(h):
    for j in range(w):
        if (i+j)%2:continue
        u=i*w+j
        for v,cap,_ in mf.to[u]:
            if cap:continue
            if v==h*w:continue
            dir=[w,-w,1,-1].index(u-v)
            ss[i][j]="^v<>"[dir]
            ss[v//w][v%w]="v^><"[dir]

print(ans)
for row in ss:print("".join(row))

Submission status: Accepted

Solution code:
N,M=map(int,input().split())
S=[list(input()) for i in range(N)]
V=N*M
G=[[] for i in range(V)]
ma=[0]*V
us=[0]*V
def ae(u,v):
  G[u].append(v)
  G[v].append(u)

def df(v):
  us[v]=1
  for i in range(len(G[v])):
    u=G[v][i]
    w=ma[u]
    if w<0 or(not(us[w]) and df(w)):
      ma[v]=u
      ma[u]=v
      return 1
  return 0

def mat():
  r=0
  for i in range(V):
    ma[i]=-1
  for v in range(V):
    if ma[v]<0:
      for i in range(V):
        us[i]=0
      if df(v):
        r+=1
  return r

w4y=[1,0,-1,0]
w4x=[0,1,0,-1]
for i in range(N):
  for j in range(M):
    if ((i+j+1)&1)and(S[i][j]=='.'):
      for k in range(4):
        k=[i+w4y[k],j+w4x[k]]
        if min(k)<0 or k[0]>=N or k[1]>=M:
          continue
        if S[k[0]][k[1]]=='.':
          ae(i*M+j,k[0]*M+k[1])
print(mat())
for i in range(N):
  for j in range(M):
    if ma[i*M+j]<0:
      continue
    if ma[i*M+j]==i*M+j+M:
      S[i][j]='v'
    elif ma[i*M+j]==i*M+j-M:
      S[i][j]='^'
    elif ma[i*M+j]==i*M+j+1:
      S[i][j]='>'
    elif ma[i*M+j]==i*M+j-1:
      S[i][j]='<'
  print(''.join(S[i]))

Submission status: Accepted

Solution code:
# D - 浮気予防
from collections import deque
from typing import List

Edge = List[int]  # type alias of Edge


class Dinic:
    """Dinic class to solve max-find_max_flow / min-cut problems.
    References:
        https://ikatakos.com/pot/programming_algorithm/graph_theory/maximum_flow
        http://vartkw.hatenablog.com/entry/2016/12/02/002703
    """

    __slots__ = ["_size", "graph", "_depth", "_progress"]

    def __init__(self, size: int) -> None:
        self._size = size
        self.graph: List[List[Edge]] = [[] for _ in range(size)]
        self._depth = [-1] * size
        self._progress = [0] * size

    def add(self, from_: int, to: int, cap: int = 1, is_directed: bool = True) -> None:
        """Add an edge."""
        self.graph[from_].append([to, cap, len(self.graph[to])])
        self.graph[to].append([from_, 0, len(self.graph[from_]) - 1])
        if not is_directed:
            self.add(to, from_, cap)

    def find_max_flow(self, source: int, target: int, inf: int = 1 << 30) -> int:
        """Find the max-find_max_flow from source to target in O(N^{2}M)."""
        flow = 0
        while True:
            self._bfs(source)
            if self._depth[target] < 0:
                break
            self._progress = [0] * self._size
            cur_flow = self._dfs(source, target, inf)
            while cur_flow:
                flow += cur_flow
                cur_flow = self._dfs(source, target, inf)
        return flow

    def _bfs(self, source: int) -> None:
        depth = [-1] * self._size
        depth[source] = 0
        queue = deque([source])
        while queue:
            v = queue.popleft()
            for to, cap, _ in self.graph[v]:
                if cap > 0 > depth[to]:
                    depth[to] = depth[v] + 1
                    queue.append(to)
        self._depth = depth

    def _dfs(self, v: int, target: int, flow: int) -> int:
        if v == target:
            return flow
        v_edges = self.graph[v]
        for i in range(self._progress[v], len(v_edges)):
            self._progress[v] = i
            to, cap, rev = v_edges[i]
            if not cap or self._depth[v] >= self._depth[to]:
                continue
            d = self._dfs(to, target, min(flow, cap))
            if not d:
                continue
            v_edges[i][1] -= d
            self.graph[to][rev][1] += d
            return d
        return 0


def practice2_d():
    # https://atcoder.jp/contests/practice2/tasks/practice2_d
    N, M = map(int, input().split())
    S = [list(input()) for _ in range(N)]
    dc = Dinic(N * M + 2)

    s = N * M
    t = N * M + 1
    for i in range(N):
        for j in range(M):
            if (i + j) % 2 == 0:
                dc.add(s, M * i + j)
            else:
                dc.add(M * i + j, t)

    for i in range(N):
        for j in range(M):
            if j + 1 < M and S[i][j] == "." and S[i][j + 1] == ".":
                u, v = M * i + j, M * i + j + 1
                if (i + j) % 2:
                    u, v = v, u
                dc.add(u, v)
            if i + 1 < N and S[i][j] == "." and S[i + 1][j] == ".":
                u, v = M * i + j, M * (i + 1) + j
                if (i + j) % 2:
                    u, v = v, u
                dc.add(u, v)

    print(dc.find_max_flow(s, t))
    for u in range(N * M + 2):
        for v, cap, _ in dc.graph[u]:
            ui, uj = divmod(u, M)
            vi, vj = divmod(v, M)
            if (
                not (ui + uj) % 2
                and not cap
                and u != s
                and u != t
                and v != s
                and v != t
            ):
                if ui + 1 == vi:
                    S[ui][uj] = "v"
                    S[vi][vj] = "^"
                elif ui == vi + 1:
                    S[ui][uj] = "^"
                    S[vi][vj] = "v"
                elif uj + 1 == vj:
                    S[ui][uj] = ">"
                    S[vi][vj] = "<"
                elif uj == vj + 1:
                    S[ui][uj] = "<"
                    S[vi][vj] = ">"

    for res in S:
        print("".join(res))


if __name__ == "__main__":
    practice2_d()

Submission status: Accepted

Solution code:
class maxflow():
    def __init__(self, n):
        self.pos = []
        self.g = [[] for _ in range(n)]
        self.n = n
    
    def add_edge(self, frm, to, cap):
        self.pos.append((frm, len(self.g[frm])))
        self.g[frm].append([to, len(self.g[to]), cap])
        self.g[to].append([frm, len(self.g[frm]) - 1, 0])
        return len(self.pos) - 1
    
    def get_edge(self, i):
        e = self.g[self.pos[i][0]][self.pos[i][1]]
        re = self.g[e[0]][e[1]]
        return (self.pos[i][0], e[0], e[2] + re[2], re[2])
    
    def edges(self):
        return [self.get_edge(i) for i in range(len(self.pos))]
    
    def change_edge(self, i, new_cap, new_flow):
        e = self.g[self.pos[i][0]][self.pos[i][1]]
        re = self.g[e[0]][e[1]]
        e[2] = new_cap - new_flow
        re[2] = new_flow
    
    def flow(self, s, t, flow_limit = -1):
        if flow_limit < 0: flow_limit = 10 ** 100
        it = [0] * self.n
        def bfs():
            L = [-1] * self.n
            L[s] = 0
            Q = [s]
            while Q:
                v = Q.pop()
                for e in self.g[v]:
                    if e[2] == 0 or L[e[0]] >= 0: continue
                    L[e[0]] = L[v] + 1
                    if e[0] == t: return L
                    Q.append(e[0])
            return L
        
        def dfs(v, up):
            if v == s: return up
            res = 0
            lv = L[v]
            for i in range(it[v], len(self.g[v])):
                e = self.g[v][i]
                if lv <= L[e[0]] or self.g[e[0]][e[1]][2] == 0: continue
                
                d = dfs(e[0], min(up - res, self.g[e[0]][e[1]][2]))
                if d <= 0: continue
                self.g[v][i][2] += d
                self.g[e[0]][e[1]][2] -= d
                res += d
                if res == up: break
            return res
        
        flow = 0
        while flow < flow_limit:
            L = bfs()
            if L[t] == -1: break
            it = [0] * self.n
            while flow < flow_limit:
                f = dfs(t, flow_limit - flow)
                if not f: break
                flow += f
        return flow
    
    def min_cut(self, s):
        visited = [0] * self.n
        Q = [s]
        while Q:
            p = Q.pop()
            visited[p] = 1
            for e in self.g[p]:
                if e[2] and visited[e[0]] == 0:
                    visited[e[0]] = 1
                    Q.append(e[0])
        return visited

N, M = map(int, input().split())
mf = maxflow(N * M + 2)
A = [[a for a in input()] for _ in range(N)]
X = [[1 if a == "." else 0 for a in aa] for aa in A]
s = N * M
t = s + 1
for i in range(N):
    for j in range(M):
        if X[i][j]:
            if (i ^ j) & 1 == 0:
                mf.add_edge(s, i * M + j, 1)
            else:
                mf.add_edge(i * M + j, t, 1)

for i in range(N - 1):
    for j in range(M):
        if X[i][j] and X[i+1][j]:
            a = i * M + j
            b = a + M
            if (i ^ j) & 1 == 0:
                mf.add_edge(a, b, 1)
            else:
                mf.add_edge(b, a, 1)
for i in range(N):
    for j in range(M - 1):
        if X[i][j] and X[i][j+1]:
            a = i * M + j
            b = a + 1
            if (i ^ j) & 1 == 0:
                mf.add_edge(a, b, 1)
            else:
                mf.add_edge(b, a, 1)

fl = mf.flow(s, t)
# print("fl =", fl)

# mc = mf.min_cut(s)
# print("mc =", mc)
# print("edges =", mf.edges())

for s, t, _, f in mf.edges():
    if f and max(s, t) < N * M:
        if s > t: s, t = t, s
        i, j = s // M, s % M
        ii, jj = t // M, t % M
        if i == ii:
            A[i][j] = ">"
            A[ii][jj] = "<"
        else:
            A[i][j] = "v"
            A[ii][jj] = "^"

print(fl)
for x in A:
    print("".join(map(str, x)))

Submission status: Accepted

Solution code:
import os
import sys

import numpy as np


def solve(n, m, sss):
    BIT_SHIFT = 9
    BIT_MASK = (1 << BIT_SHIFT) - 1

    def combine_rc(r, c):
        return (r << BIT_SHIFT) | c

    def separate_rc(rc):
        return rc >> BIT_SHIFT, rc & BIT_MASK

    DINIC_LINKS = []

    def dinic_init(n):
        lst = [np.empty(0, dtype=np.int64)]
        lst.clear()
        DINIC_LINKS.append([lst.copy() for _ in range(n)])
        return len(DINIC_LINKS) - 1

    def dinic_add_link(ins, frm, to, cap):
        links = DINIC_LINKS[ins]
        links[frm].append(np.array([to, cap, len(links[to])], np.int64))
        links[to].append(np.array([frm, 0, len(links[frm]) - 1], np.int64))

    def dinic_bfs(ins, n, s):
        links = DINIC_LINKS[ins]
        depth = np.full(n, -1, dtype=np.int64)
        depth[s] = 0
        deq = np.zeros(n + 5, dtype=np.int64)
        dl, dr = 0, 1
        deq[0] = s
        while dl < dr:
            v = deq[dl]
            dl += 1
            for link in links[v]:
                if link[1] > 0 and depth[link[0]] == -1:
                    depth[link[0]] = depth[v] + 1
                    deq[dr] = link[0]
                    dr += 1
        return depth

    def dinic_dfs(ins, depth, progress, s, t):
        links = DINIC_LINKS[ins]
        stack = [(s, 10 ** 18)]
        flow = 0
        while stack:
            v, f = stack.pop()
            if v == t:
                flow = f
                continue
            if flow == 0:
                i = progress[v]
                if i == len(links[v]):
                    continue
                progress[v] += 1
                stack.append((v, f))
                to, cap, rev = links[v][i]
                if cap == 0 or depth[v] >= depth[to]:
                    continue
                stack.append((to, min(f, cap)))
            else:
                i = progress[v] - 1
                link = links[v][i]
                link[1] -= flow
                links[link[0]][link[2]][1] += flow
        return flow

    def dinic_maximum_flow(ins, n, s, t):
        flow = 0
        while True:
            depth = dinic_bfs(ins, n, s)
            if depth[t] == -1:
                return flow
            progress = np.zeros(n, dtype=np.int64)
            path_flow = dinic_dfs(ins, depth, progress, s, t)
            while path_flow != 0:
                flow += path_flow
                path_flow = dinic_dfs(ins, depth, progress, s, t)

    lefts = []
    rights = []
    rights_rev = {}

    for r in range(1, n + 1):
        for c in range(1, m + 1):
            if sss[r, c]:
                continue
            rc = combine_rc(r, c)
            if (r ^ c) & 1:
                lefts.append(rc)
            else:
                rights_rev[rc] = len(rights)
                rights.append(rc)
    l_size = len(lefts)
    r_size = len(rights)
    s = l_size + r_size
    t = s + 1
    total_size = t + 1

    ins = dinic_init(total_size)

    MOVE = (-(1 << BIT_SHIFT), -1, 1, 1 << BIT_SHIFT)

    for i in range(l_size):
        rc = lefts[i]
        for drc in MOVE:
            nrc = rc + drc
            if nrc in rights_rev:
                dinic_add_link(ins, i, rights_rev[nrc] + l_size, 1)
        dinic_add_link(ins, s, i, 1)
    for j in range(r_size):
        dinic_add_link(ins, j + l_size, t, 1)

    flow = dinic_maximum_flow(ins, total_size, s, t)
    links = DINIC_LINKS[ins]

    for i in range(l_size):
        for link in links[i]:
            if link[0] == s or link[1] == 1:
                continue
            j = link[0] - l_size
            ir, ic = separate_rc(lefts[i])
            jr, jc = separate_rc(rights[j])
            if ir == jr:
                if ic > jc:
                    ic, jc = jc, ic
                sss[ir, ic] = 4
                sss[jr, jc] = 5
            else:
                if ir > jr:
                    ir, jr = jr, ir
                sss[ir, ic] = 2
                sss[jr, jc] = 3
            break

    return flow


SIGNATURE = '(i8,i8,i1[:,:],)'
if sys.argv[-1] == 'ONLINE_JUDGE':
    from numba.pycc import CC

    cc = CC('my_module')
    cc.export('solve', SIGNATURE)(solve)
    cc.compile()
    exit()

if os.name == 'posix':
    # noinspection PyUnresolvedReferences
    from my_module import solve
else:
    from numba import njit

    solve = njit(SIGNATURE, cache=True)(solve)
    print('compiled', file=sys.stderr)

n, m = map(int, input().split())
sss = np.ones((n + 2, m + 2), dtype=np.int8)
for i in range(n):
    sss[i + 1, 1:m + 1] = list(map('.#'.index, input()))

ans = solve(n, m, sss)
print(ans)
CHARS = '.#v^><'.__getitem__
for i in range(1, n + 1):
    print(''.join(map(CHARS, sss[i, 1:m + 1])))