#include <atcoder/all>
#include <bits/stdc++.h>

using namespace std;
using namespace atcoder;
typedef long long ll;
typedef long double Double;
#define rep(i, n) for (int i = 0; i < (int)(n); i++)

// chmax, chmin
template<class T>
inline bool chmax(T &a, T b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}

template<class T>
inline bool chmin(T &a, T b) {
    if (a > b) {
        a = b;
        return 1;
    }
    return 0;
}


namespace MCF {
    struct Edge {
        using capacity_type = int;
        using cost_type = int;

        static cost_type inf_cost() {
            return 1000000;
        }

        capacity_type cap;
        cost_type cost;
        int to, rev;

        Edge() {}

        Edge(capacity_type c, int t, cost_type cost, int r) : cap(c), to(t), cost(cost), rev(r) {}
    };

    typedef vector<vector<Edge>> Graph;

    void addEdge(Graph &g, int from, int to, Edge::capacity_type cap, Edge::cost_type cost) {
        g[from].push_back(Edge(cap, to, cost, g[to].size()));
        g[to].push_back(Edge(0, from, -cost, g[from].size() - 1)); //rev edge
    }

// O(VE+FElogV)
    Edge::cost_type minCostFlow(vector<vector<Edge>> &g, int s, int t, Edge::capacity_type f, bool negative = false) {
        const int n = g.size();
        Edge::cost_type res = 0;
        vector<Edge::cost_type> h(n, 0);
        vector<int> prevv(n), preve(n);

        if (negative) {
            h.assign(n, Edge::inf_cost());
            h[s] = 0;

            bool update = true;
            while (update) {
                update = false;
                for (int v = 0; v < n; v++) {
                    if (h[v] == Edge::inf_cost()) continue;
                    for (int i = 0; i < g[v].size(); i++) {
                        auto &e = g[v][i];
                        if (e.cap > 0 && h[e.to] > h[v] + e.cost) {
                            h[e.to] = h[v] + e.cost;
                            prevv[e.to] = v;
                            preve[e.to] = i;
                            update = true;
                        }
                    }
                }
            }
        }

        while (f > 0) {
            typedef pair<Edge::cost_type, int> P;
            priority_queue<P, vector<P>, greater<> > que;
            vector<Edge::cost_type> dist(n, Edge::inf_cost());
            dist[s] = 0;
            que.push(P(0, s));

            while (!que.empty()) {
                P p = que.top();
                que.pop();
                int v = p.second;
                if (dist[v] < p.first) continue;
                if (v == t) break;
                for (int i = 0; i < g[v].size(); i++) {
                    auto &e = g[v][i];
                    if (e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]) {
                        dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
                        prevv[e.to] = v;
                        preve[e.to] = i;
                        que.push(P(dist[e.to], e.to));
                    }
                }
            }

            if (dist[t] == Edge::inf_cost()) return -1;
            for (int v = 0; v < n; v++) h[v] += dist[v];

            Edge::capacity_type d = f;
            for (int v = t; v != s; v = prevv[v]) {
                d = min(d, g[prevv[v]][preve[v]].cap);
            }

            f -= d;//
            res += d * h[t];

            for (int v = t; v != s; v = prevv[v]) {
                auto &e = g[prevv[v]][preve[v]];
                e.cap -= d;
                g[v][e.rev].cap += d;
            }
        }
        return res;
    }
}

class MCFGraph {
private:
    MCF::Graph g;
    int org_size; // the original size of graph excluding start / goal

    int in(int x) {
        assert(x < org_size);
        return x * 2;
    }

    int out(int x) {
        assert(x < org_size);
        return x * 2 + 1;
    }

    void add_edge_raw(int from, int to, MCF::Edge::capacity_type cap, MCF::Edge::cost_type cost) {
        MCF::addEdge(g, from, to, cap, cost);
    }

    int start() {
        return g.size() - 2;
    }

    int goal() {
        return g.size() - 1;
    }

public:

    MCFGraph(int n) : g(MCF::Graph(2 * n + 2)), org_size(n) {
        // doubled vertices by default to support vertex flow
    }

    void add_edge(int from, int to, MCF::Edge::capacity_type cap, MCF::Edge::cost_type cost) {
        add_edge_raw(out(from), in(to), cap, cost);
    }

    void add_start_edge(int to, MCF::Edge::capacity_type cap, MCF::Edge::cost_type cost) {
        add_edge_raw(start(), in(to), cap, cost);
    }

    void add_goal_edge(int from, MCF::Edge::capacity_type cap, MCF::Edge::cost_type cost) {
        add_edge_raw(out(from), goal(), cap, cost);
    }

    void add_vertex(int v, MCF::Edge::capacity_type cap, MCF::Edge::cost_type cost) {
        MCF::addEdge(g, in(v), out(v), cap, cost);
    }

    MCF::Edge::cost_type min_cost_flow(MCF::Edge::capacity_type f, bool negative = false) {
        return MCF::minCostFlow(g, start(), goal(), f, negative);
    }
};

void solve(long long N, long long M, std::vector<std::string> S) {
    int n = N * M;
    MCFGraph graph(n);
    auto id = [&](int i, int j) {
        return i * M + j;
    };
    int o = 0;
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < M; j++) {
            graph.add_vertex(id(i, j), 1000000, 0);
            if (S[i][j] == '.') {
                graph.add_goal_edge(id(i, j), 1, 0);
//                cout << id(i, j) << "goal" << endl;
            } else if (S[i][j] == 'o') {
                graph.add_start_edge(id(i, j), 1, 0);
                graph.add_goal_edge(id(i, j), 1, 0);
//                cout << id(i, j) << "start" << endl;
                o++;
            }
        }
    }
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < M; j++) {
            if (S[i][j] == '#') continue;

            if (i + 1 < N) {
                if (S[i + 1][j] != '#') {
                    graph.add_edge(id(i, j), id(i + 1, j), 1000000, -1);
                }
            }
            if (j + 1 < M) {
                if (S[i][j + 1] != '#') {
                    graph.add_edge(id(i, j), id(i, j + 1), 1000000, -1);
                }
            }
        }
    }
    auto res = graph.min_cost_flow(o, true);
    cout << -res << endl;
}

int main() {
    long long N;
    scanf("%lld", &N);
    long long M;
    scanf("%lld", &M);
    std::vector<std::string> S(N);
    for (int i = 0; i < N; i++) {
        std::cin >> S[i];
    }
    solve(N, M, std::move(S));
    return 0;
}


Measured memory_bytes: 4372