#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using PII = pair<int, int>;
using VI = vector<int>;
#define fst first
#define snd second

#define FOR(i, a, b) for (int i = a; i < b; i++)
#define REP(i, n) FOR(i, 0, n)
#define SZ(a) ((int) ((a).size()))
#define ALL(x) (x).begin(), (x).end()
#define SET(a, v) memset((a), (v), sizeof(a))
#define EB emplace_back
#define PB push_back
#define MP make_pair
#define ST first
#define ND second


template<typename FLOW, typename COST>
struct MCMF {
  const COST INFC = 1e9, EPSC = 0;
  const FLOW INFF = 1e9, EPSF = 0;
  struct Edge {
    int from, to;
    FLOW flow, cap;
    COST cost;
  };
  int nodes, src, dest, m = 0;
  vector<vector<int>> adj;
  vector<Edge> edges;
  void add_edge(int u, int v, FLOW cap, COST cost) {
    edges.PB({u, v, 0, cap, cost});
    adj[u].PB(m++);
    edges.PB({v, u, 0, 0, -cost});
    adj[v].PB(m++);
  }
  vector<COST> dis;
  vector<bool> inQ;
  VI par;
  pair<FLOW, COST> SPFA() {
    fill(ALL(dis), INFC);
    fill(ALL(inQ), false);
    queue<int> Q;
    dis[src] = 0;
    Q.push(src);
    inQ[src] = true;
    while (!Q.empty()) {
      int u = Q.front();
      Q.pop();
      inQ[u] = false;
      for (int i : adj[u]) {
        auto &e = edges[i];
        if (e.cap - e.flow > EPSF && dis[e.to] - (dis[u] + e.cost) > EPSC) {
          dis[e.to] = dis[u] + e.cost;
          par[e.to] = i;
          if (!inQ[e.to]) {
            Q.push(e.to);
            inQ[e.to] = true;
          }
        }
      }
    }
    if (dis[dest] + EPSC >= INFC) return MP(0, 0);
    FLOW aug = INFF;
    for (int u = dest; u != src; u = edges[par[u]].from) {
      aug = min(aug, edges[par[u]].cap - edges[par[u]].flow);
    }
    for (int u = dest; u != src; u = edges[par[u]].from) {
      edges[par[u]].flow += aug;
      edges[par[u] ^ 1].flow -= aug;
    }
    return MP(aug, aug * dis[dest]);
  }
  MCMF(int n, int s, int t)
      : nodes(n), src(s), dest(t), adj(n), dis(n), inQ(n), par(n) {}
  pair<FLOW, COST> mincostmaxflow() {
    pair<FLOW, COST> ans(0, 0);
    while (true) {
      auto cur = SPFA();
      if (cur.first <= EPSF) break;
      ans.first += cur.first;
      ans.second += cur.second;
    }
    return ans;
  }
};


int main() {
#ifdef LOCAL_EXEC
//	freopen("sample.in", "r", stdin);
//	freopen("sample.out", "w", stdout);
#else
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
#endif

  int n, m;
  cin >> n >> m;
  vector<string> grid(n);
  for (auto &row : grid)
    cin >> row;

  const int src = n * m, snk = n * m + 1;
  auto mcmf = MCMF<int, int>(n * m + 2, src, snk);

  auto idx = [&](int i, int j) { return i * m + j; };

  const int inf = n * m;

  for (int i = 0; i < n; i++) {
    for (int j = 0; j < m; j++) {
      if (grid[i][j] == '#') continue;
      mcmf.add_edge(idx(i, j), snk, 1, 0);

      if (grid[i][j] == 'o') mcmf.add_edge(src, idx(i, j), 1, 0);

      if (i + 1 < n && grid[i + 1][j] != '#') {
        mcmf.add_edge(idx(i, j), idx(i + 1, j), inf, -1);
      }
      if (j + 1 < m && grid[i][j + 1] != '#') {
        mcmf.add_edge(idx(i, j), idx(i, j + 1), inf, -1);
      }
    }
  }

  int ans = -mcmf.mincostmaxflow().snd;
  cout << ans << endl;

  return 0;
}


Measured memory_bytes: 3908