#include <bits/stdc++.h>
  
using namespace std;
  
template<class T> void ckmin(T &a, T b) { a = min(a, b); }
template<class T> void ckmax(T &a, T b) { a = max(a, b); }
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
  
#define pb push_back
#define mp make_pair
#define cotu cout
#define itn int
#define Red ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define F first
#define S second
#define sz(x) (int)x.size()
#define all(x) (x).begin(), (x).end()
#define rep(i, n) for(int i = 0; i < n; ++i)
#define repr(i,n) for(int i = n - 1; i >= 0; --i)
#define Rep(i, a, n) for(int i = (a); i <=(n); ++i)
#define repst(i, n) for(auto it = n.begin(); it != n.end(); ++it)
#define Repr(i, a, n) for(int i = (n); i >= (a); --i)
#define setp(x) fixed << setprecision(x)
#define ordered_set tree<int , null_type,less<int >, rb_tree_tag,tree_order_statistics_node_update> 
 
#include <ext/pb_ds/assoc_container.hpp> 
#include <ext/pb_ds/tree_policy.hpp> 
using namespace __gnu_pbds; 
  
typedef long long ll;
typedef pair<ll, ll> pll;
typedef pair<int, int> pii;
  
const int inf = int(1e9);
const int N = 1e6 + 555; // TL MB
const double PI = acos(-1.0);
// void mul(ll &x, ll y){
// 	x *= y;
// 	if(x >= mod) x %= mod;
// } 
// ll bp(ll a, ll n){
// 	ll r = 1;
// 	while(n){
// 		if(n & 1) mul(r, a);
// 		mul(a, a);
// 		n >>= 1;
// 	}
// 	return r; 
// }
template<typename T_vector> // neal
void output_vector(const T_vector &v, bool add_one = false, int start = -1, int end = -1) {
    if (start < 0) start = 0;
    if (end < 0) end = int(v.size());
 
    for (int i = start; i < end; i++)
        cout << v[i] + (add_one ? 1 : 0) << (i < end - 1 ? ' ' : '\n');
}

struct Edge
{
    int from, to, capacity, cost;
    Edge() {}
    Edge(int from, int to, int capacity, int cost) : from(from), to(to), capacity(capacity), cost(cost) {}
};

vector<vector<int>> adj, cost, capacity;

const int INF = 1e9;

int n, m;
void shortest_paths(int n, int v0, vector<int>& d, vector<int>& p) {
    d.assign(n, INF);
    d[v0] = 0;
    vector<bool> inq(n, false);
    queue<int> q;
    q.push(v0);
    p.assign(n, -1);

    while (!q.empty()) {
        int u = q.front();
        q.pop();
        inq[u] = false;
        for (int v : adj[u]) {
            if (capacity[u][v] > 0 && d[v] > d[u] + cost[u][v]) {
                d[v] = d[u] + cost[u][v];
                p[v] = u;
                if (!inq[v]) {
                    inq[v] = true;
                    q.push(v);
                }
            }
        }
    }
}

int min_cost_flow(int N, vector<Edge> edges, int K, int s, int t) {
    adj.assign(N, vector<int>());
    cost.assign(N, vector<int>(N, 0));
    capacity.assign(N, vector<int>(N, 0));
    for (Edge e : edges) {
        adj[e.from].push_back(e.to);
        adj[e.to].push_back(e.from);
        cost[e.from][e.to] = e.cost;
        cost[e.to][e.from] = -e.cost;
        capacity[e.from][e.to] = e.capacity;
    }
    int flow = 0;
    int cost = 0;
    vector<int> d, p;
    while (flow < K) {
        shortest_paths(N, s, d, p);
        if (d[t] == INF)
            break;

        // find max flow on that path
        int f = K - flow;
        int cur = t;
        while (cur != s) {
            f = min(f, capacity[p[cur]][cur]);
            cur = p[cur];
        }

        // apply flow
        flow += f;
        cost += f * d[t];
        cur = t;
        while (cur != s) {
            capacity[p[cur]][cur] -= f;
            capacity[cur][p[cur]] += f;
            cur = p[cur];
        }
    }

    return cost;
}
void solve()
{ 
	cin >> n >> m;
	int sink = 0, flow = n * m + n + m;
	char c[n][m];
	vector<Edge> v;
	rep(i, n) rep(j, m){
		cin >> c[i][j];
	}
	rep(i, n){
		rep(j, m){
			if(c[i][j] == 'o') {
				v.pb(Edge(sink, i * m + j + 1, 1, 0));
			}
		}
	}
	rep(i, n){
		rep(j, m){
			if(c[i][j] != 'o') continue;
			int id = i * m + j + 1;
			vector<vector<int> > u(n, vector<int> (m));
			queue<pair<int, pii> > q;
			q.push({i, {j, 0}});
			u[i][j] = 1;
			while(!q.empty()){
				pair<int, pii> p = q.front();
				q.pop();
				int curid = p.S.F + p.F * m + 1;
				Edge e1(id, curid, 1, -p.S.S);
				// Edge e2(curid, id, 1, p.S.S);
				v.pb(e1); //v.pb(e2);
				if(p.S.F + 1 < m and p.S.S + 1 > u[p.F][p.S.F + 1] and c[p.F][p.S.F + 1] != '#') {
					u[p.F][p.S.F + 1] = p.S.S + 1;
					q.push({p.F, {p.S.F + 1, p.S.S + 1}});
				}
				if(p.F + 1 < n and p.S.S + 1 > u[p.F + 1][p.S.F] and c[p.F + 1][p.S.F] != '#'){
					u[p.F + 1][p.S.F] = p.S.S + 1;
					q.push({p.F + 1, {p.S.F, p.S.S + 1}});
				}
			}
		}
	}
	rep(i, n){
		rep(j, m){
			if(c[i][j] == '.' || c[i][j] == 'o') {
				v.pb(Edge(i * m + j + 1, flow, 1, 0));
			}
		}
	}
	cout << -min_cost_flow(flow + 2, v, inf, sink, flow);
}
	


int main()
{
	// freopen("input.txt", "r", stdin);
	// freopen("output.txt", "w", stdout);
    // Red;
  	int T = 1;
  	// cin >> T;
  	for(int i = 1; i <= T; ++i){
  		solve();
  	}
  	return 0;

}

Measured memory_bytes: 59352