#include <bits/stdc++.h>
#define LL long long
#define PII pair<int, int>
#define PLL pair<LL, LL>
#define all_of(v) (v).begin(), (v).end()
#define sort_unique(c) (sort(c.begin(),c.end()), c.resize(distance(c.begin(),unique(c.begin(),c.end()))))
#define fi first
#define se second
//const LL INF = (LL) 1e9 + 8763;
//const LL MOD = (LL) 1e9 + 7;
using namespace std;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
//#define int LL
const int MAXV = 2505;
const int INF = 1e8;

struct Min_cost_flow {
	// 0-based
	#define fi first
	#define se second
	struct Edge {
	    int to,cap;
		int cost,rev;
	};
	
	int V,E;
	vector<Edge> G[MAXV];
	PII pre[MAXV];
	int flow[MAXV], dist[MAXV];
	bool inque[MAXV];
	
	void init(int _V) {
	    V=_V;
	    for (int i=0;i<=V;i++) G[i].clear();
	}
	PII add_edge(int fr, int to, int cap, int cost) {
		int a = G[fr].size(), b = G[to].size();
	    G[fr].push_back({to,cap,cost,b});
	    G[to].push_back({fr,0,-cost,a});
	    return {fr, (int) G[fr].size() - 1};
	}
	bool SPFA(int s, int t, int &ans_flow, int &ans_cost) {
		queue<int> que;
	    for (int i=0;i<=V;i++) {
	        dist[i]=INF;
	        inque[i]=false;
	    }
	    dist[s]=0;
	    flow[s]=INF;
	    inque[s]=true;
	    que.push(s);
	    while (!que.empty()) {
	        int v=que.front(); que.pop();
	        inque[v]=false;
	        for (int i=0;i<G[v].size();i++) {
	        	const Edge &e = G[v][i];
	            if (e.cap>0 && dist[v]+e.cost<dist[e.to]) {
	                flow[e.to]=min(flow[v],e.cap);
	                dist[e.to]=dist[v]+e.cost;
	                pre[e.to]={v,i};
	                if (!inque[e.to]) que.push(e.to),inque[e.to]=true;
	            }
	        }
	    }
	    if (dist[t]==INF) return false;
	    //if (dist[t]>=0) return false;
	    // Add above line -> min cost first, then max flow (priority)
	    // Without        -> max flow first, then min cost
	
	    int v=t,f=flow[t];
	    ans_flow+=flow[t];
	    ans_cost+=(dist[t]*flow[t]);
	    while (v!=s) {
	    	Edge &e = G[pre[v].fi][pre[v].se];
	        e.cap-=f;
	        G[v][e.rev].cap+=f;
	        v=pre[v].fi;
	    }
	    return true;
	}
	pair<int,int> mcmf(int s, int t) {
	    int ans_flow=0, ans_cost=0;
	    while (SPFA(s,t,ans_flow,ans_cost));
	    return make_pair(ans_flow,ans_cost);
	}
} algo;

const int MAXN = 55;
int n, m, vis[MAXN][MAXN];
char mp[MAXN][MAXN];

void solve() {
	cin >> n >> m;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			cin >> mp[i][j];
		}
	}
	
	int src = n * m, tgt = src + 1;
	algo.init(tgt + 1);
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			if (mp[i][j] == 'o') {
				algo.add_edge(src, i * m + j, 1, 0);
			}
			if (mp[i][j] != '#') {
				if (i != n - 1 && mp[i + 1][j] != '#') {
					algo.add_edge(i * m + j, (i + 1) * m + j, INF, -1);
				}
				if (j != m - 1 && mp[i][j + 1] != '#') {
					algo.add_edge(i * m + j, i * m + (j + 1), INF, -1);
				}
				algo.add_edge(i * m + j, tgt, 1, 0);
			}
		}
	}
	
	auto res = algo.mcmf(src, tgt);
//	cout << "Flow " << res.fi << '\n';
	cout << -res.se << '\n';
}

signed main() {
	ios_base::sync_with_stdio(0); cin.tie(0);

	int tc = 1;
//	cin >> tc;
	for (int i = 1; i <= tc; i++) {
		solve();
	}

	return 0;
} 

Measured memory_bytes: 4124