#include <bits/stdc++.h>

using namespace std;
using int64 = long long;

constexpr int DEBUG = 0;

struct CostFlowEdge {
  const int from, to;
  const int64 cap;
  const int64 cost;
  CostFlowEdge(int from, int to, int64 cap, int64 cost)
      : from(from), to(to), cap(cap), cost(cost) {}
};

// Returns a pair of (flow, cost).
pair<int64, int64> MinCostFlow(
    int n, const vector<CostFlowEdge>& edges, int s, int t, int64 flow_cap) {
  constexpr int64 INF = INT64_MAX / 4;  // Update this when it doesn't work.

  int m = edges.size();
  
  vector<vector<tuple<CostFlowEdge, int>>> graph(n);
  vector<int64> res_caps(2 * m);  // Edge ID to residual cap
  vector<int64> costs(2 * m);  // Edge ID to original cost
  vector<int64> res_costs(2 * m);  // Edge ID to residual cost
  vector<int> to_reverse(2 * m, -1);  // Edge ID to the reverse edge ID

  for (int i = 0; i < m; i++) {
    const auto& edge = edges[i];
    graph[edge.from].push_back(make_tuple(edge, i));
    graph[edge.to].push_back(
        make_tuple(CostFlowEdge(edge.to, edge.from, 0, -edge.cost), m + i));
    res_caps[i] = edge.cap;
    costs[i] = edge.cost;
    costs[m + i] = -edge.cost;
    res_costs[i] = edge.cost;
    res_costs[m + i] = -edge.cost;
    to_reverse[i] = m + i;
    to_reverse[m + i] = i;
  }

  int64 total_flow = 0;
  int64 total_cost = 0;
  struct State {
    int v, parent, last_edge;
    int64 weight;
  };
  auto compare_state = [](State s1, State s2) {
    return s1.weight > s2.weight;
  };
  while (total_flow < flow_cap) {
    priority_queue<State, vector<State>, decltype(compare_state)>
        pq(compare_state);
    pq.push(State({s, -1, -1, 0}));
    vector<int64> distances(n, INF);
    vector<int> parents(n, -1);
    vector<int> last_edges(n, -1);
    vector<int> visited(n);
    while(!pq.empty()) {
      State s = pq.top();
      pq.pop();
      if (visited[s.v]) continue;
      visited[s.v] = true;
      distances[s.v] = s.weight;
      parents[s.v] = s.parent;
      last_edges[s.v] = s.last_edge;
      for (const auto& edge_tuple : graph[s.v]) {
        const auto& edge = get<0>(edge_tuple);
        int edge_id = get<1>(edge_tuple);
        if (res_caps[edge_id] == 0 || visited[edge.to]) continue;
        State next =
            State({edge.to, s.v, edge_id, s.weight + res_costs[edge_id]});
        pq.push(next);
      }
    }

    if (distances[t] == INF) return make_pair(total_flow, total_cost);

    int cur = t;
    vector<int> path;
    while (cur != s) {
      path.push_back(last_edges[cur]);
      cur = parents[cur];
    }

    int64 inc = flow_cap - total_flow;
    for (int edge_id : path) {
      inc = min(inc, res_caps[edge_id]);
    }

    total_flow += inc;

    for (int edge_id : path) {
      total_cost += costs[edge_id] * inc;
      res_caps[edge_id] -= inc;
      res_caps[to_reverse[edge_id]] += inc;
    }

    for (int i = 0; i < m; i++) {
      res_costs[i] += distances[edges[i].from] - distances[edges[i].to];
      res_costs[m + i] -= distances[edges[i].from] - distances[edges[i].to];
    }
  }
  
  return make_pair(total_flow, total_cost);
}

constexpr int COST_OFFSET = 1000;

template<typename T>
vector<vector<T>> Make2DVector(int d1, int d2, T default_value) {
  return vector<vector<T>>(d1, vector<T>(d2, default_value));
}

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);

  int h, w;
  cin >> h >> w;
  vector<string> board(h);
  for (int r = 0; r < h; r++) {
    cin >> board[r];
  }

  int s = 2 * h * w;
  int t = 2 * h * w + 1;

  auto source_id_fn = [&](int r, int c) -> int {
    return r * w + c;
  };
  auto sink_id_fn = [&](int r, int c) -> int {
    return h * w + r * w + c;
  };

  vector<CostFlowEdge> edges;
  for (int y = 0; y < h; y++) {
    for (int x = 0; x < w; x++) {
      if (board[y][x] == 'o') {
        edges.push_back(CostFlowEdge(s, source_id_fn(y, x), 1, 0));

        vector<vector<int>> dp = Make2DVector(h, w, 0);
        dp[y][x] = 1;
        for (int y1 = y; y1 < h; y1++) {
          for (int x1 = x; x1 < w; x1++) {
            if (dp[y1][x1]) {
              if (y1 + 1 < h && board[y1 + 1][x1] != '#') {
                dp[y1 + 1][x1] = 1;
              }
              if (x1 + 1 < w && board[y1][x1 + 1] != '#') {
                dp[y1][x1 + 1] = 1;
              }
            }
          }
        }

        for (int y1 = y; y1 < h; y1++) {
          for (int x1 = x; x1 < w; x1++) {
            if (dp[y1][x1]) {
              int distance = y1- y + x1 - x;
              // cout << y << " " << x << " " << y1 << " " << x1 << endl;
              // cout << "distance: " << distance << endl;
              edges.push_back(
                  CostFlowEdge(source_id_fn(y, x), sink_id_fn(y1, x1), 1, COST_OFFSET - distance));
            }
          }
        }
      }
    }
  }

  for (int y = 0; y < h; y++) {
    for (int x = 0; x < w; x++) {
      edges.push_back(CostFlowEdge(sink_id_fn(y, x), t, 1, 0));
    }
  }

  int64 flow, cost;
  tie(flow, cost) = MinCostFlow(2 * h * w + 2, edges, s, t, h * w);
  // cout << flow << " " << cost << endl;
  cout << flow * COST_OFFSET - cost << endl;
}

Measured memory_bytes: 19080