#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
using namespace std;

#define ff               first
#define ss               second
#define pb               push_back
#define all(x)           (x).begin(), (x).end()
#define mem0(x)          memset(x, 0, sizeof (x))
#define mem1(x)          memset(x, -1, sizeof (x))

typedef long long ll;
typedef vector <int> vi;
typedef vector<vi> vvi;
typedef pair<int, int> ii;
typedef vector <ii> vii;
typedef vector<vii> vvii;
typedef vector <ll> vll;
typedef vector<vll> vvll;
typedef pair<ll, ll> pll;

#define rep(i, n) for (int i = 0; i < (int)n; i++)
#define repx(i, a, b) for (int i = (int)a; i < (int)b; i++)

vector <string> b;
int vis[50][50];
int n, m;
map <ii, int> mp;

class Hungarian
{
    int inf = numeric_limits<int>::max() / 2;
    bool maxi, swapped = false;
    vector<vector<int>> cost;
    vector<int> p, way;
    vector<int> u, v;
    int l, r;

public:
    // left/right == partition sizes
    Hungarian(int left, int right, bool maximizing)
    {
        l = left, r = right, maxi = maximizing;
        if (swapped = l > r) swap(l, r);
        cost.assign(l + 1, vector<int>(r + 1, 0));
        u.assign(l + 1, 0); v.assign(r + 1, 0);
        p.assign(r + 1, 0); way.assign(r + 1, 0);
    }

    void add_edge(int l, int r, int w)
    {
        assert(l and r);  // indices start from 1 !!
        if (swapped) swap(l, r);
        cost[l][r] = maxi ? -w : w;
    }

    // execute after all edges were added
    void calculate()
    {
        repx(i, 1, l + 1)
        {
            vector<bool> used(r+1, false);
            vector<int> minv(r+1, inf);
            int j0 = 0; p[0] = i;

            while (p[j0])
            {
                int j1, i0 = p[j0]; used[j0] = true;
                int delta = inf;
                repx(j, 1, r + 1) if (not used[j])
                    {
                        int cur = cost[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;
                    }
                rep(j, r + 1)
                {
                    if (used[j]) u[p[j]] += delta, v[j] -= delta;
                    else minv[j] -= delta;
                }
                j0 = j1;
            }

            while (j0) p[j0] = p[way[j0]], j0 = way[j0];
        }
    }

    // execute after executing calculate()
    int answer() { return maxi ? v[0] : -v[0]; }

    bool are_matched(int l, int r)
    {
        if (swapped) swap(l, r);
        return p[r] == l;
    }
    void addEdges(int u, int i, int j, int cst){
        if (i >= n || j >= m || vis[i][j] || b[i][j] == '#') return;
        vis[i][j] = 1;
        if (b[i][j] == '.') add_edge(u, mp[{i,j}], cst);
        addEdges(u, i+1, j, cst+1);
        addEdges(u, i, j+1, cst+1);
    }
};




int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> m;
    int lsz=0, rsz=0;
    for (int i = 0; i < n; ++i) {
        string s; cin >> s;
        b.pb(s);
        for (int j = 0; j < m; ++j) {
            if (s[j] == 'o') {
                lsz++;
                mp[{i,j}] = lsz;
            }
            else if (s[j] == '.') {
                rsz++;
                mp[{i,j}] = rsz;
            }
        }
    }
    Hungarian HG(lsz, rsz, 1);
    for (int k = 0; k < n; ++k) {
        for (int i = 0; i < m; ++i) {
            if (b[k][i] == 'o'){
                mem0(vis);
                HG.addEdges(mp[{k,i}], k, i, 0);
            }
        }
    }
    HG.calculate();
    cout << HG.answer() << "\n";
}

Measured memory_bytes: 4416