#define _USE_MATH_DEFINES
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
#define SZ(x) ((int)((x).size()))

template <typename T1, typename T2>
string print_iterable(T1 begin_iter, T2 end_iter, int counter) {
    bool done_something = false;
    stringstream res;
    res << "[";
    for (; begin_iter != end_iter and counter; ++begin_iter) {
        done_something = true;
        counter--;
        res << *begin_iter << ", ";
    }
    string str = res.str();
    if (done_something) {
        str.pop_back();
        str.pop_back();
    }
    str += "]";
    return str;
}

vector<int> SortIndex(int size, std::function<bool(int, int)> compare) {
    vector<int> ord(size);
    for (int i = 0; i < size; i++) ord[i] = i;
    sort(ord.begin(), ord.end(), compare);
    return ord;
}

template <typename T>
bool MinPlace(T& a, const T& b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}

template <typename T>
bool MaxPlace(T& a, const T& b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}

template <typename S, typename T>
ostream& operator <<(ostream& out, const pair<S, T>& p) {
    out << "{" << p.first << ", " << p.second << "}";
    return out;
}

template <typename T>
ostream& operator <<(ostream& out, const vector<T>& v) {
    out << "[";
    for (int i = 0; i < (int)v.size(); i++) {
        out << v[i];
        if (i != (int)v.size()-1) out << ", ";
    }
    out << "]";
    return out;
}

template<class TH>
void _dbg(const char* name, TH val){
    clog << name << ": " << val << endl;
}
template<class TH, class... TA>
void _dbg(const char* names, TH curr_val, TA... vals) {
    while(*names != ',') clog << *names++;
    clog << ": " << curr_val << ", ";
    _dbg(names+1, vals...);
}

#if DEBUG && !ONLINE_JUDGE
    ifstream input_from_file("input.txt");
    #define cin input_from_file

    #define dbg(...) _dbg(#__VA_ARGS__, __VA_ARGS__)
    #define dbg_arr(x, len) clog << #x << ": " << print_iterable(x, x+len, -1) << endl;
#else
    #define dbg(...)
    #define dbg_arr(x, len)
#endif

// Implementation of the Hungarian algorithm for the matching of minimum
// cost.
// Complexity: N³
//
// This source code was heavily inspired (stands for copy-pasted) from:
//      https://e-maxx.ru/algo/assignment_hungary

#include <vector>
#include <limits>
using namespace std;

// This class accepts a complete weighted bipartite graph (as a NxM matrix)
// and solves the assignment problem.
// The costs can be both positive or negative (this way also the max-cost
// problem can be handled).
// The matrix need not to be a square-matrix, but it must hold N <= M.
// Hence the matching will always have size equal to N.
// The class is templatized over T that is the type of the costs.
//
// To compute the minimum cost call GetMinCostMatching().
// If you are also interested in the actual matching, just look at the
// prec vector, that associates to any column its row (prec[c] = N if the
// column is not matched).

template <typename T>
struct Hungarian {
    const T INF = numeric_limits<T>::max()/2;

    int N, M;
    const vector<vector<T>>& aa; // the cost matrix NxM.
    // Instead of having sizes N and M, the sizes are N+1 and M+1.
    // The reason is that we add a fictitious row and column (with indexes N and
    // M) because the algorithm is much shorter in this way.
    vector<T> u, v; // potentials
    // prec stores the matching col->row
    // way is used internally
    vector<int> prec, way;

    // ACTHUNG: It must hold N <= M.
    Hungarian(int N, int M, const vector<vector<T>>& aa):
        N(N), M(M), aa(aa), u(N+1, 0), v(M+1, 0), prec(M+1, N), way(M+1, M) {}    

    // Returns the minimum cost of a maximum matching.
    T GetMinCostMatching() {
        for (int i = 0; i < N; i++) {
            prec[M] = i;
            int j0 = M;
            vector<T> minv (M+1, INF);
            vector<bool> used (M+1, false);

            do {
                used[j0] = true;
                int i0 = prec[j0], j1;
                T delta = INF;
                for (int j = 0; j < M; j++) {
                    if (!used[j]) {
                        T cur = aa[i0][j]-u[i0]-v[j];
                        if (cur < minv[j]) minv[j] = cur,  way[j] = j0;
                        if (minv[j] < delta) delta = minv[j],  j1 = j;
                    }
                }
                for (int j = 0; j <= M; j++) {
                    if (used[j]) u[prec[j]] += delta,  v[j] -= delta;
                    else minv[j] -= delta;
                }
                j0 = j1;
            } while (prec[j0] != N);
            do {
                int j1 = way[j0];
                prec[j0] = prec[j1];
                j0 = j1;
            } while (j0 != M);
        }
        return -v[M];
    }
};

///////////////////////////////////////////////////////////////////////////
//////////////////// DO NOT TOUCH BEFORE THIS LINE ////////////////////////
///////////////////////////////////////////////////////////////////////////

const int MAXN = 55;
char mat[MAXN][MAXN];
bool visited[MAXN][MAXN];
int N, M;
void dfs(int x, int y) {
    if (visited[x][y] or mat[x][y] == '#') return;
    visited[x][y] = 1;
    if (x < N-1) dfs(x+1, y);
    if (y < M-1) dfs(x, y+1);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); // Remove in problems with online queries!

    cin >> N >> M;

    for (int i = 0; i < N; i++) {
        for (int j = 0; j < M; j++) {
            cin >> mat[i][j];
        }
    }

    vector<vector<int>> aa;
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < M; j++) {
            if (mat[i][j] != 'o') continue;
            dfs(i, j);
            vector<int> vec;
            for (int x = 0; x < N; x++) for (int y = 0; y < M; y++) {
                if (visited[x][y]) vec.push_back(-((x-i)+(y-j)));
                else vec.push_back(1e5); 
                visited[x][y] = 0;
            }
            aa.push_back(vec);
        }
    }

    cout << -Hungarian(aa.size(), N*M, aa).GetMinCostMatching() << "\n";
}


Measured memory_bytes: 4308