#pragma GCC target ("avx2")
#pragma GCC optimize ("unroll-loops")
#pragma GCC optimize ("O3")
#include "bits/stdc++.h"
#include <unordered_set>
#include <unordered_map>
#include <random>
using namespace std;
typedef long long ll;
const ll MOD = /*1'000'000'007LL;*/ 998'244'353LL;
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(), (x).end()
#define rep(i, n) for(int (i)=0; (i)<(n); (i)++)
const int dx[4]={ 1,0,-1,0 };
const int dy[4]={ 0,1,0,-1 };

struct MinCostFlow{
private:
    typedef pair<int, int> P;
    struct edge{ int to, cap; long long cost; int rev, num; bool isrev; };

    int V;
    vector<vector<edge>> G;
    vector<long long> h, dist;
    vector<int> prevv, preve;
    const long long INF = 1LL<<62;

public:
    vector<int> edges;

    MinCostFlow(int _V=0){
        V = _V;
        G.resize(V);
        h.resize(V, 0);
        dist.resize(V);
        prevv.resize(V);
        preve.resize(V);
    }

    void add(int from, int to, int cap, long long cost){
        G[from].push_back({ to,cap,cost,(int)G[to].size(),(int)edges.size(),false });
        G[to].push_back({ from,0,-cost,(int)G[from].size()-1,(int)edges.size(),true });
        edges.push_back(0);
    }

    long long calculate(int s, int t, int f=INT_MAX){
        long long ret = 0;
        while(f > 0){
            priority_queue<P, vector<P>, greater<P>> que;
            dist.assign(V, INF);
            dist[s] = 0;
            que.push({ 0,s });
            while(!que.empty()){
                P p = que.top(); que.pop();
                int v = p.second;
                if(dist[v] < p.first) continue;
                for(int i=0; i<G[v].size(); i++){
                    edge& 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({ dist[e.to],e.to });
                    }
                }
            }
            if(dist[t] == INF) break;
            for(int v=0; v<V; v++) h[v] += dist[v];

            int d = f;
            for(int v=t; v!=s; v=prevv[v]){
                d = min(d, G[prevv[v]][preve[v]].cap);
            }
            f -= d;
            ret += d*h[t];
            for(int v=t; v!=s; v=prevv[v]){
                edge& e = G[prevv[v]][preve[v]];
                e.cap -= d;
                G[v][e.rev].cap += d;
                if(!e.isrev) edges[e.num] += d;
                else edges[e.num] -= d;
            }
        }
        return ret;
    }

    vector<pair<int, long long>> calculate_slope(int s, int t){
        int f = 0; long long cost = 0;
        vector<pair<int, long long>> ret;
        while(true){
            priority_queue<P, vector<P>, greater<P>> que;
            dist.assign(V, INF);
            dist[s] = 0;
            que.push({ 0,s });
            while(!que.empty()){
                P p = que.top(); que.pop();
                int v = p.second;
                if(dist[v] < p.first) continue;
                for(int i=0; i<G[v].size(); i++){
                    edge& 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({ dist[e.to],e.to });
                    }
                }
            }
            if(dist[t] == INF) break;
            for(int v=0; v<V; v++) h[v] += dist[v];

            int d = INT_MAX;
            for(int v=t; v!=s; v=prevv[v]){
                d = min(d, G[prevv[v]][preve[v]].cap);
            }
            f += d;
            cost += d*h[t];
            ret.push_back({ f, cost });
            for(int v=t; v!=s; v=prevv[v]){
                edge& e = G[prevv[v]][preve[v]];
                e.cap -= d;
                G[v][e.rev].cap += d;
                if(!e.isrev) edges[e.num] += d;
                else edges[e.num] -= d;
            }
        }
        return ret;
    }
};

int H, W;
string S[50];

signed main(){
    cin >> H >> W;
    rep(i, H) cin >> S[i];

    MinCostFlow f(1+H*W+1);
    rep(i, H){
        rep(j, W){
            if(i+1 < H && S[i][j] != '#' && S[i+1][j] != '#'){
                f.add(1+i*W+j, 1+(i+1)*W+j, 10000, -1);
            }
            if(j+1 < W && S[i][j] != '#' && S[i][j+1] != '#'){
                f.add(1+i*W+j, 1+i*W+j+1, 10000, -1);
            }
        }
    }
    rep(i, H){
        rep(j, W){
            if(S[i][j] == 'o'){
                f.add(0, 1+i*W+j, 1, 0);
            }
            if(S[i][j] != '#'){
                f.add(1+i*W+j, 1+H*W, 1, 0);
            }
        }
    }
    cout << -f.calculate(0, 1+H*W) << endl;
}

Measured memory_bytes: 4336