using namespace std;

#define visual

#ifdef visual
#include <iostream>
#include <fstream>
#include <vector>
#include <queue>
#include <algorithm>
#include <set>
#include <map>
#include <assert.h>
#include <functional>
#include <math.h>
#include <string>
#include <ctime>
#endif
#ifndef visual
#include <bits/stdc++.h>
#endif

typedef long long ll;

struct edge
{
	int to, flow, cap, cost, rev;
};

#ifndef INT_MAX
#define INT_MAX 10000000
#endif

struct MinCostMaxFlow
{
	int nodes;
	vector<int> prio, curflow, prevedge, prevnode, q, pot;
	vector<bool> inqueue;
	vector<vector<edge> > graph;
	MinCostMaxFlow() {}

	MinCostMaxFlow(int n) : nodes(n), prio(n, 0), curflow(n, 0),
		prevedge(n, 0), prevnode(n, 0), q(n, 0), pot(n, 0), inqueue(n, 0), graph(n) {}

	void addEdge(int source, int to, int capacity, int cost)
	{
		edge a = { to, 0, capacity, cost, (int)graph[to].size() };
		edge b = { source, 0, 0, -cost, (int)graph[source].size() };
		graph[source].push_back(a);
		graph[to].push_back(b);
	}

	void bellman_ford(int source, vector<int>& dist)
	{
		fill(dist.begin(), dist.end(), INT_MAX);
		dist[source] = 0;
		int qt = 0;
		q[qt++] = source;
		for (int qh = 0; (qh - qt) % nodes != 0; qh++)
		{
			int u = q[qh % nodes];
			inqueue[u] = false;
			for (auto& e : graph[u])
			{
				if (e.flow >= e.cap)
					continue;
				int v = e.to;
				int newDist = dist[u] + e.cost;
				if (dist[v] > newDist)
				{
					dist[v] = newDist;
					if (!inqueue[v])
					{
						inqueue[v] = true;
						q[qt++ % nodes] = v;
					}
				}
			}
		}
	}

	pair<int, int> minCostFlow(int source, int dest, int maxflow)
	{
		bellman_ford(source, pot);
		int flow = 0;
		int flow_cost = 0;
		while (flow < maxflow)
		{
			priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > q;
			q.push({ 0, source });
			fill(prio.begin(), prio.end(), INT_MAX);
			prio[source] = 0;
			curflow[source] = INT_MAX;
			while (!q.empty())
			{
				int d = q.top().first;
				int u = q.top().second;
				q.pop();
				if (d != prio[u])
					continue;
				for (int i = 0; i < graph[u].size(); i++)
				{
					edge& e = graph[u][i];
					int v = e.to;
					if (e.flow >= e.cap)
						continue;
					int newPrio = prio[u] + e.cost + pot[u] - pot[v];
					if (prio[v] > newPrio)
					{
						prio[v] = newPrio;
						q.push({ newPrio, v });
						prevnode[v] = u;
						prevedge[v] = i;
						curflow[v] = min(curflow[u], e.cap - e.flow);
					}
				}
			}
			if (prio[dest] == INT_MAX)
				break;
			for (int i = 0; i < nodes; i++)
				pot[i] += prio[i];
			int df = min(curflow[dest], maxflow - flow);
			flow += df;
			for (int v = dest; v != source; v = prevnode[v])
			{
				edge& e = graph[prevnode[v]][prevedge[v]];
				e.flow += df;
				graph[v][e.rev].flow -= df;
				flow_cost += df * e.cost;
			}
		}
		return { flow, flow_cost };
	}
};


int main() {
    ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);

	int n, m;
	cin >> n >>  m;
	MinCostMaxFlow f(n * m + 2);
	vector<string> g;
	for (int i = 0; i < n; i++) {
		string s;
		cin >> s;
		g.push_back(s);
	}
	int k = 0;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			if (j != m - 1 && g[i][j] != '#' && g[i][j + 1] != '#') {
				f.addEdge(i * m + j + 1, i * m + j + 2, 100, -1);
			}
			if (i != n - 1 && g[i][j] != '#' && g[i+1][j] != '#') {
                f.addEdge(i * m + j + 1, i * m + j + m + 1, 100, -1);
			}
			if (g[i][j] == 'o') {
				k++;
                f.addEdge(0, i * m + j + 1, 1, 0);
			}
            f.addEdge(i * m + j + 1, n * m + 1, 1, 0);
		}
	}
	auto ans = f.minCostFlow(0, n * m + 1, k);
	cout << -ans.second << endl;
}


Measured memory_bytes: 4116