#include <iostream>
#include <algorithm>
#include <iomanip>
#include <vector>
#include <queue>
#include <set>
#include <map>

#define debug_value(x) cerr << "line" << __LINE__ << ":<" << __func__ << ">:" << #x << "=" << x << endl;
#define debug(x) cerr << "line" << __LINE__ << ":<" << __func__ << ">:" << x << endl;

using namespace std;
typedef long long ll;

int H, W;
char s[50][50];

const int N_MAX = 5000;
const ll INF = 1e18;

struct edge{
    int to;
    ll cap, cost;
    int rev;
};

int V;  // 頂点数
vector<edge> G[N_MAX];
ll dist[N_MAX];
int prevv[N_MAX], preve[N_MAX];

void add_edge(int from, int to, ll cap, ll cost){
    G[from].push_back((edge){to, cap, cost, G[to].size()});
    G[to].push_back((edge){from, 0, -cost, G[from].size()-1});
}

// Vを初期化してからつかうこと
ll min_cost_flow(int s, int t, int f){
    ll res = 0;
    while(f > 0){
        fill(dist, dist+V, INF);
        dist[s] = 0;
        bool update = true;
        while(update){
            update = false;
            for(int v = 0; v < V; v++){
                if(dist[v] == INF) 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){
                        dist[e.to] = dist[v]+e.cost;
                        prevv[e.to] = v;
                        preve[e.to] = i;
                        update = true;
                    }
                }
            }
        }
        if(dist[t] == INF){
            return -1;
        }
        ll d = f;
        for(int v = t; v != s; v = prevv[v]){
            d = min(d, G[prevv[v]][preve[v]].cap);
        }
        f -= d;
        res += d*dist[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;
        }
    }
    return res;
}

int dh[2] = {0, 1};
int dw[2] = {1, 0};

bool is_in_field(int i, int j){
    return i >= 0 && i < H && j >= 0 && j < W;
}

bool used[50][50];

void clear_used(){
    for(int i = 0;i < H; i++){
        for(int j = 0; j < W; j++){
            used[i][j] = false;
        }
    }
}

void dfs(int h, int w){
    used[h][w] = true;
    for(int i = 0; i < 2; i++){
        int h_to = h+dh[i];
        int w_to = w+dw[i];
        if(is_in_field(h_to, w_to) && !used[h_to][w_to] && (s[h_to][w_to] != '#')){
            dfs(h_to, w_to);
        }
    }
}

int to_idx(int h, int w){
    return h*W+w;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout << setprecision(10) << fixed;
    cin >> H >> W;
    V = H*W+2;
    for(int i = 0; i < H; i++){
        for(int j = 0; j < W; j++){
            cin >> s[i][j];
            add_edge(to_idx(i, j)+1, H*W+1, 1, 0);
        }
    }
    int sum = 0;
    int cnt = 0;
    for(int i = 0; i < H; i++){
        for(int j = 0; j < W; j++){
            if(s[i][j] == 'o') {
                cnt++;
                dfs(i, j);
                sum += (i+j);
                add_edge(0, 1+to_idx(i, j), 1, 0);
                for(int h = 0; h < H; h++){
                    for(int w = 0; w < W; w++){
                        if(used[h][w]){
                            // cout << i << ' ' << j << ' ' << h << ' ' << w << endl;
                            add_edge(1+to_idx(i, j), 1+to_idx(h, w), 1, -(h+w-(i+j)));
                        }
                    }
                }
                clear_used();
            }
        }
    }
    cout << -min_cost_flow(0, H*W+1, cnt) << endl;
}

Measured memory_bytes: 9480