#include<iostream>
#include<string>
#include<algorithm>
#include<vector>
#include<iomanip>
#include<math.h>
#include<complex>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<functional>
#include<assert.h>
#include<numeric>
using namespace std;
#define REP(i,m,n) for(int i=(int)(m) ; i < (int) (n) ; ++i )
#define rep(i,n) REP(i,0,n)
using ll = long long;
constexpr int inf=1e9+7;
constexpr ll longinf=1LL<<60 ;
constexpr ll mod=1e9+7 ;


struct PrimalDual{
    struct edge{
        int to;ll cap, cost;int rev;
    };
    int n;
    const ll INF = numeric_limits<ll>::max();
    vector<vector<edge>> v;
    vector<ll> pot, dist;
    vector<pair<int,int>> par;
 
    PrimalDual(int n):n(n),v(n),pot(n){}
 
    void add_edge(int from, int to, ll cap, ll cost) {
        v[from].emplace_back((edge) {to, cap, cost, (int) v[to].size()});
        v[to].emplace_back((edge) {from, 0, -cost, (int) v[from].size() - 1});
    }
 
    pair<ll,ll> minimum_road(int s,int t){
        par.assign(n,{-1,-1});
        dist.assign(n,INF);
        dist[s] = 0;
        using P = pair<ll,ll>;
        priority_queue<P,vector<P>,greater<P>> pq;
        pq.emplace(0,s);
        while(!pq.empty()){
            P p = pq.top();pq.pop();
            if(dist[p.second]<p.first)continue;
            rep(i,v[p.second].size()){
                edge& e = v[p.second][i];
                ll nd = dist[p.second]+e.cost+pot[p.second]-pot[e.to];
                if(e.cap>0 && dist[e.to]>nd){
                    dist[e.to] = nd;
                    par[e.to] = {p.second, i};
                    pq.emplace(dist[e.to],e.to);
                }
            }
        }
        if(dist[t]==INF)return {-1,-1};
        rep(i,n)pot[i] += dist[i];
        ll f = INF;
        int cur = t;
        while(cur != s){
            int p = par[cur].first, j = par[cur].second;
            f = min(f, v[p][j].cap);
            cur = p;
        }
        cur = t;
        while(cur != s){
            int p = par[cur].first, j = par[cur].second;
            v[p][j].cap -= f;
            v[cur][v[p][j].rev].cap += f;
            cur = p;
        }
        return {pot[t],f};
    }
 
    ll minimum_cost_flow(int s, int t, ll k){
        ll ret = 0;
        while(k){
            pair<ll,ll> z = minimum_road(s, t);
            if (z.first < 0)return -1;
            if (k<=z.second){
                ret+=z.first*k;
                break;
            }
            k -= z.second;
            ret += z.first*z.second;
        }
        return ret;
    }
};

int dist[50][50];
int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    int n,m;
    cin>>n>>m;
    vector<string> s(n);
    rep(i,n)cin>>s[i];
    PrimalDual pd(n*m + 2 + 100);
    int st = n*m, en = n*m+1;
    int id = n*m+2;
    int cnt = 0;
    rep(x,n)rep(y,m){
        if(s[x][y]!='o')continue;
        rep(i,n)rep(j,m)dist[i][j]=inf;
        dist[x][y]=0;
        rep(i,n)rep(j,m){
            if(i>0)dist[i][j]=min(dist[i][j],dist[i-1][j]+1);
            if(j>0)dist[i][j]=min(dist[i][j],dist[i][j-1]+1);
            if(s[i][j]=='#')dist[i][j]=inf;
        }
        pd.add_edge(st, id, 1, 0);
        rep(i,n)rep(j,m){
            if(dist[i][j]!=inf)pd.add_edge(id, i*m+j, 1, 1000-dist[i][j]);
        }
        cnt++;
        id++;
    }
    rep(i,n*m)pd.add_edge(i, en, 1, 0);
    int ret = pd.minimum_cost_flow(st, en, cnt);
    cout << 1000 * cnt - ret << endl;
    return 0;
}

Measured memory_bytes: 9564