package main

import (
	"bufio"
	"container/heap"
	"fmt"
	"os"
)

func main() {
	N, M := ReadInt(), ReadInt()
	grid := make([]string, N)
	for y := 0; y < N; y++ {
		grid[y] = ReadString()
	}

	sources := 0
	for y := 0; y < N; y++ {
		for x := 0; x < M; x++ {
			if grid[y][x] == 'o' {
				sources++
			}
		}
	}

	s, t := 0, 1+sources+N*M
	adjL = make([][]*Edge, 2+sources+N*M)

	toID := func(x, y int) int { return 1 + sources + x + y*M }
	sourceNum := 0

	var dx = []int{1, 0}
	var dy = []int{0, 1}

	for y := 0; y < N; y++ {
		for x := 0; x < M; x++ {
			if grid[y][x] != '#' {
				for i := 0; i < 2; i++ {
					nx, ny := x+dx[i], y+dy[i]
					if 0 <= nx && nx < M && 0 <= ny && ny < N && grid[ny][nx] != '#' {
						AddEdge(toID(x, y), toID(nx, ny), 10000, -1)
					}
				}
				AddEdge(toID(x, y), t, 1, 0)
			}
			if grid[y][x] == 'o' {
				sourceNum++
				AddEdge(sourceNum, toID(x, y), 1, 0)
			}
		}
	}

	for i := 1; i <= sourceNum; i++ {
		AddEdge(s, i, 1, 0)
	}
	fmt.Println(-MinCostFlow(s, t, sources))
}

const Inf = 1 << 60

type Edge struct {
	To, Cap, Cost, Rev int
}

var adjL [][]*Edge

// 最小費用流 O(F|E|log|V|)
// sからtへの流量fの最小費用流を求める。流せない場合は-1
func MinCostFlow(s, t, f int) int {
	V := len(adjL)
	res := 0
	h, prevv, preve := make([]int, V), make([]int, V), make([]int, V)
	for f > 0 {
		q := Heapq{}
		dist := make([]int, V)
		for i := 0; i < V; i++ {
			dist[i] = Inf
		}
		dist[s] = 0
		q.push(State{Dist: 0, Node: s})
		for len(q) > 0 {
			now := q.pop()
			v := now.Node
			if dist[v] < now.Dist {
				continue
			}
			for i, edge := range adjL[v] {
				if edge.Cap > 0 && dist[edge.To] > dist[v]+edge.Cost+h[v]-h[edge.To] {
					dist[edge.To] = dist[v] + edge.Cost + h[v] - h[edge.To]
					prevv[edge.To] = v
					preve[edge.To] = i
					q.push(State{Dist: dist[edge.To], Node: edge.To})
				}
			}
		}
		if dist[t] == Inf {
			return -1
		}
		for v := 0; v < V; v++ {
			h[v] += dist[v]
		}
		d := f
		for v := t; v != s; v = prevv[v] {
			d = Min(d, adjL[prevv[v]][preve[v]].Cap)
		}
		f -= d
		res += d * h[t]
		for v := t; v != s; v = prevv[v] {
			edge := adjL[prevv[v]][preve[v]]
			edge.Cap -= d
			adjL[v][edge.Rev].Cap += d
		}
	}
	return res
}

func AddEdge(from, to, cap, cost int) {
	adjL[from] = append(adjL[from], &Edge{
		To:   to,
		Cap:  cap,
		Cost: cost,
		Rev:  len(adjL[to]),
	})
	adjL[to] = append(adjL[to], &Edge{
		To:   from,
		Cap:  0,
		Cost: -cost,
		Rev:  len(adjL[from]) - 1,
	})
}

func Min(xs ...int) int {
	min := xs[0]
	for _, x := range xs[1:] {
		if min > x {
			min = x
		}
	}
	return min
}

type State struct {
	Dist, Node int
}
type Heapq []State

func (q Heapq) Less(i, j int) bool    { return q[i].Dist < q[j].Dist }
func (q Heapq) Len() int              { return len(q) }
func (q Heapq) Swap(i, j int)         { q[i], q[j] = q[j], q[i] }
func (q *Heapq) Push(x interface{})   { *q = append(*q, x.(State)) }
func (q *Heapq) Pop() (x interface{}) { *q, x = (*q)[:len(*q)-1], (*q)[len(*q)-1]; return }
func (q *Heapq) push(v State)         { heap.Push(q, v) }
func (q *Heapq) pop() State           { return heap.Pop(q).(State) }

var reader = bufio.NewReader(os.Stdin)

func Scan(a ...interface{}) {
	if _, err := fmt.Fscan(reader, a...); err != nil {
		panic(err)
	}
}
func ReadInt() (i int)       { Scan(&i); return }
func ReadString() (s string) { Scan(&s); return }
func ReadInts(n int) (a []int) {
	for i := 0; i < n; i++ {
		a = append(a, ReadInt())
	}
	return
}


Measured memory_bytes: 10620