#include <bits/stdc++.h>
using namespace std;

template<typename T, typename C>
struct mcmf{
	static constexpr T eps = (T) 1e-9;
	struct Edge{
		int from, to;
		T capacity, flow;
		C cost;
	};
	vector<vector<int>> adj;
	vector<Edge> edge;
	vector<C> d;
	vector<bool> in_queue;
	vector<int> q, pe;
	int n, source, sink;
	T flow = 0;
	C cost = 0;
	mcmf(int n, int source, int sink): n(n), source(source), sink(sink), adj(n), d(n), in_queue(n), pe(n){ }
	void clear(){
		for(auto &e: edge) e.flow = 0;
		flow = 0;
	}
	int insert(int from, int to, T forward_cap, T backward_cap, C cost){
		assert(0 <= from && from < n && 0 <= to && to < n);
		int ind = int(edge.size());
		adj[from].push_back((int)edge.size());
		edge.push_back({from, to, forward_cap, 0, cost});
		adj[to].push_back((int)edge.size());
		edge.push_back({to, from, backward_cap, 0, -cost});
		return ind;
	}
	bool expath(){
		fill(d.begin(), d.end(), numeric_limits<C>::max());
		q.clear();
		q.push_back(source);
		d[source] = 0;
		in_queue[source] = true;
		int beg = 0;
		bool found = false;
		while(beg < (int)q.size()){
			int i = q[beg ++];
			if(i == sink) found = true;
			in_queue[i] = false;
			for(int id : adj[i]){
				const auto &e = edge[id];
				if(e.capacity - e.flow > eps && d[i] + e.cost < d[e.to]){
					d[e.to] = d[i] + e.cost;
					pe[e.to] = id;
					if(!in_queue[e.to]){
						q.push_back(e.to);
						in_queue[e.to] = true;
					}
				}
			}
		}
		if(found){
			T push = numeric_limits<T>::max();
			int v = sink;
			while(v != source){
				const auto &e = edge[pe[v]];
				push = min(push, e.capacity - e.flow);
				v = e.from;
			}
			v = sink;
			while(v != source){
				auto &e = edge[pe[v]];
				e.flow += push;
				auto &back = edge[pe[v] ^ 1];
				back.flow -= push;
				v = e.from;
			}
			flow += push;
			cost += push * d[sink];
		}
		return found;
	}
	pair<T, C> get_mcmf(){
		while(expath()){ }
		return {flow, cost};
	}
};

int main(){
	cin.tie(0)->sync_with_stdio(0);
	cin.exceptions(ios::badbit | ios::failbit);
	int n, m;
	cin >> n >> m;
	vector<string> a(n);
	for(auto &s: a){
		cin >> s;
	}
	int src = 2 * n * m, snk = src + 1;
	static const vector<pair<int, int>> dr{{1, 0}, {0, 1}};
	mcmf<int, int> F(2 * n * m + 2, src, snk);
	for(auto i = 0; i < n; ++ i){
		for(auto j = 0; j < m; ++ j){
			int u = i * m + j;
			if(a[i][j] != '#'){
				F.insert(u + n * m, snk, 1, 0, - i - j);
			}
			if(a[i][j] == 'o'){
				F.insert(src, u, 1, 0, i + j);
				static vector<vector<int>> vis(n, vector<int>(m));
				int it = u + 1;
				function<void(int, int)> dfs = [&](int x, int y){
					vis[x][y] = it;
					int v = x * m + y;
					F.insert(u, v + n * m, 1, 0, 0);
					for(auto [dx, dy]: dr){
						int nx = x + dx, ny = y + dy;
						if(nx < n && ny < m && vis[nx][ny] ^ it && a[nx][ny] != '#'){
							dfs(nx, ny);
						}
					}
				};
				dfs(i, j);
			}
		}
	}
	cout << -F.get_mcmf().second << "\n";
	return 0;
}

/*

*/

////////////////////////////////////////////////////////////////////////////////////////
//                                                                                    //
//                                   Coded by Aeren                                   //
//                                                                                    //
////////////////////////////////////////////////////////////////////////////////////////

Measured memory_bytes: 9492