#include <bits/stdc++.h>
using namespace std;
#define SZ(a) ((int)(a).size())
typedef long long int64;
struct Edge {
  int from, to, cap, flow, cost;
  Edge(int u, int v, int c, int f, int w) : from(u), to(v), cap(c), flow(f), cost(w) {}
};
const int INF = 1e9;
const int maxn = 13000;
struct MCMF {
  int n, m;
  vector<Edge> edges;
  vector<long long> G[maxn];
  int inq[maxn];
  int d[maxn];
  int p[maxn];
  int a[maxn];

  void init(int x) {
    this->n = x;
    for (int i = 0; i < n; i++) G[i].clear();
    edges.clear();
  }

  void AddEdge(int from, int to, int cap, int cost) {
    edges.push_back(Edge(from, to, cap, 0, cost));  //有向边
    edges.push_back(Edge(to, from, 0, 0, -cost));
    m = edges.size();
    G[from].push_back(m - 2);
    G[to].push_back(m - 1);
  }

  bool BellmanFord(int s, int t, int &flow, int &cost) {
    for (int i = 0; i < n; i++) d[i] = INF;
    memset(inq, 0, sizeof(inq));
    d[s] = 0;
    inq[s] = 1;
    p[s] = 0;
    a[s] = INF;

    queue<int> Q;
    Q.push(s);
    while (!Q.empty()) {
      int u = Q.front();
      Q.pop();
      inq[u] = 0;
      for (int i = 0; i < SZ(G[u]); i++) {
        Edge &e = edges[G[u][i]];
        if (e.cap > e.flow && d[e.to] > d[u] + e.cost) {
          d[e.to] = d[u] + e.cost;
          p[e.to] = G[u][i];
          a[e.to] = min(a[u], e.cap - e.flow);
          if (!inq[e.to]) {
            Q.push(e.to);
            inq[e.to] = 1;
          }
        }
      }
    }
    if (d[t] == INF) return false;
    flow += a[t];
    cost += d[t] * a[t];
    for (int u = t; u != s; u = edges[p[u]].from) {
      edges[p[u]].flow += a[t];
      edges[p[u] ^ 1].flow -= a[t];
    }
    return true;
  }

  int MaxcostMaxflow(int s, int t, int &cost) {
    int flow = 0;
    cost = 0;
    while (BellmanFord(s, t, flow, cost))
      ;
    return flow;
  }
};
int main() {
#ifdef LOCAL
  freopen(".a.in", "r", stdin);
#endif
  ios_base::sync_with_stdio(false), cout.tie(0), cin.tie(0);
  int n, m;
  cin >> n >> m;
  vector<string> S(n);
  vector<vector<int>> L(n, vector<int>(m)), R(n, vector<int>(m));
  for (int i = 0; i < n; ++i) cin >> S[i];
  int s = 0, t = 1, tot = 2;
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < m; ++j)
      if (S[i][j] == 'o') {
        L[i][j] = tot++;
        R[i][j] = tot++;
      } else if (S[i][j] == '.') {
        R[i][j] = tot++;
      }
  }
  MCMF F;
  F.init(tot);
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < m; ++j) {
      if (L[i][j]) {
        F.AddEdge(s, L[i][j], 1, 0);
        auto bfs = [&](int x, int y) {
          vector<vector<int>> vis(n, vector<int>(m, -1));
          vis[x][y] = 0;
          vector<pair<int, int>> P;
          P.emplace_back(x, y);
          for (int q = 0; q < SZ(P); ++q) {
            auto [a, b] = P[q];
            F.AddEdge(L[x][y], R[a][b], 1, -vis[a][b]);
            if (a + 1 < n && S[a + 1][b] != '#' && vis[a + 1][b] == -1) {
              vis[a + 1][b] = vis[a][b] + 1;
              P.emplace_back(a + 1, b);
            }
            if (b + 1 < m && S[a][b + 1] != '#' && vis[a][b + 1] == -1) {
              vis[a][b + 1] = vis[a][b] + 1;
              P.emplace_back(a, b + 1);
            }
          }
        };
        bfs(i, j);
      }
      if (R[i][j]) {
        F.AddEdge(R[i][j], t, 1, 0);
      }
    }
  }
  int cost, flow;
  flow = F.MaxcostMaxflow(s, t, cost);
  //cout<<flow<<'\n';
  cout << -cost << '\n';
  return 0;
}
//2020.09.20 22:26:37


Measured memory_bytes: 10252