//Original Code:
//#include <self/utility>
//#include <self/debug>
//#include <self/mcmf>
//using namespace std;
//int n,m;
//char c[55][55];
//const int dx[]={0,1};
//const int dy[]={1,0};
//int dist[55][55];
//int id[55][55];
//int rhsNum;
//vector <pii> vec[105];
//
//inline bool out(int x,int y)
//{
//	return (x>=n || y>=m);
//}
//
//inline int getID(int x,int y)
//{
//	return (id[x][y]==-1?id[x][y]=rhsNum++:id[x][y]);
//}
//
//inline void bfs(int sx,int sy,int id)
//{
//	queue <tuple<int,int> > que;
//	que.push(make_tuple(sx,sy));
//	for(int i=0;i<n;i++)
//	{
//		for(int j=0;j<m;j++)
//		{
//			dist[i][j]=-inf;
//		}
//	}
////	memset(dist,-1,sizeof(dist));
//	dist[sx][sy]=0;
//	while(que.size())
//	{
//		int x,y;
//		tie(x,y)=que.front();
//		que.pop();
//		for(int i=0;i<2;i++)
//		{
//			int nx=x+dx[i];
//			int ny=y+dy[i];
//			if(out(nx,ny) || dist[nx][ny]!=-inf || c[nx][ny]=='#') continue;
//			dist[nx][ny]=dist[x][y]+1;
//			que.push(make_tuple(nx,ny));
//		}
//	}
//	vector <tuple<int,int,int> > vSort;
//	for(int i=0;i<n;i++)
//	{
//		for(int j=0;j<n;j++)
//		{
//			vSort.push_back(make_tuple(dist[i][j],i,j));
//		}
//	}
//	sort(all(vSort));
//	reverse(all(vSort));
//	for(int i=0;i<min(100,(int)vSort.size());i++)
//	{
//		vec[id].push_back(mp(getID(get<1>(vSort[i]),get<2>(vSort[i])),get<0>(vSort[i])));
//	}
//}
//
//int main()
//{
////	freopen("input.txt","r",stdin);
//	memset(id,-1,sizeof(id));
//	cin>>n>>m;
//	for(int i=0;i<n;i++)
//	{
//		scanf("%s",c[i]);
//	}
//	NetworkWithCost g;
//	g.init(n*m+2);
//	int src=n*m,dst=src+1;
//	g.feedsd(src,dst);
//	for(int i=0;i<n;i++)
//	{
//		for(int j=0;j<m;j++)
//		{
//			if(c[i][j]=='#') continue;
//			if(!out(i+1,j) && c[i+1][j]!='#')
//			{
//				g.addEdge(i*m+j,(i+1)*m+j,inf,0);
//			}
//			if(!out(i,j+1) && c[i][j+1]!='#')
//			{
//				g.addEdge(i*m+j,(i)*m+(j+1),inf,0);
//			}
//			if(c[i][j]=='o') g.addEdge(src,i*m+j,1,i+j);
//			g.addEdge(i*m+j,dst,1,-i-j);
//		}
//	}
//	MCMF d;
//	d.init(g);
//	cout<<-d.run().second<<endl;
//	return 0;
//}

//substituted with C++ Inliner
#ifndef _SELF_UTILITY
#define _SELF_UTILITY 1
#include <numeric>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <map>
#include <queue>
#include <set>
#include <string>
#include <string.h>
#include <stack>
#include <assert.h>
#include <bitset>
#include <time.h>
#define Endl endl
#define mp make_pair
#define ll long long 
#define ull unsigned long long
#define pii pair<int,int>
#define over(A) {cout<<A<<endl;exit(0);}
#define all(A) A.begin(),A.end()
#define quickcin ios_base::sync_with_stdio(false);
const int mod=998244353;
const int gmod=2;
const int inf=1039074182;
const ll llinf=1LL*inf*inf;
template <typename T1,typename T2> inline void chmin(T1 &x,T2 b) {if(b<x) x=b;}
template <typename T1,typename T2> inline void chmax(T1 &x,T2 b) {if(b>x) x=b;}
template <typename T1,typename T2> inline void chadd(T1 &x,T2 b) {x+=b;if(x>=mod) x-=mod;}
template <typename T1,typename T2> inline void chmul(T1 &x,T2 b) {x=1LL*x*b%mod;}
template <typename T1,typename T2> inline void chmod(T1 &x,T2 b) {x%=b,x+=b;if(x>=b) x-=b;}
#endif

#ifndef _SELF_DEBUG
#define _SELF_DEBUG 1
#ifndef _SELF_OPERATOR
#define _SELF_OPERATOR 1
using namespace std;
template <typename T>
ostream & operator << (ostream& cout,vector<T> vec)
{
	cout<<"{";
	for(int i=0;i<(int)vec.size();i++)
	{
		cout<<vec[i];
		if(i!=(int)vec.size()-1) cout<<',';
	}
	cout<<"}";
	return cout;
}

template <typename T1,typename T2>
ostream & operator << (ostream & cout,pair<T1,T2> p)
{
	cout<<"("<<p.first<<','<<p.second<<")";
	return cout;
}

template <typename T,typename T2>
ostream & operator << (ostream & cout,set <T,T2> s)
{
	vector <T> t;
	for(auto x:s) t.push_back(x);
	cout<<t;
	return cout;
}

template <typename T>
ostream & operator << (ostream & cout,multiset <T> s)
{
	vector <T> t;
	for(auto x:s) t.push_back(x);
	cout<<t;
	return cout;
}

template <typename T>
ostream & operator << (ostream &cout,queue <T> q)
{
	vector <T> t;
	while(q.size())
	{
		t.push_back(q.front());
		q.pop();
	}
	cout<<t;
	return cout;
}

template <typename T1,typename T2>
ostream & operator << (ostream &cout,map <T1,T2> m)
{
	for(auto &x:m)
	{
		cout<<"Key: "<<x.first<<' '<<"Value: "<<x.second<<endl;
	}
	return cout;
}

template <typename T>
T operator * (vector <T> v1,vector <T> v2)
{
	assert(v1.size()==v2.size());
	int n=v1.size();
	T res=0;
	for(int i=0;i<n;i++)
	{
		res+=v1[i]*v2[i];
	}
	return res;
}

template <typename T1,typename T2>
pair<T1,T2> operator + (pair<T1,T2> x,pair<T1,T2> y)
{
	return make_pair(x.first+y.first,x.second+y.second);
}

template <typename T1,typename T2>
void operator += (pair<T1,T2> &x,pair<T1,T2> y)
{
	x.first+=y.first;
	x.second+=y.second;
}

template <typename T1,typename T2>
pair<T1,T2> operator - (pair<T1,T2> x)
{
	return make_pair(-x.first,-x.second);
}

template <typename T>
vector<vector<T> > operator ~ (vector<vector <T> > vec)
{
	vector <vector<T> > v;
	int n=vec.size(),m=vec[0].size();
	v.resize(m);
	for(int i=0;i<m;i++)
	{
		v[i].resize(n);
	}
	for(int i=0;i<m;i++)
	{
		for(int j=0;j<n;j++)
		{
			v[i][j]=vec[j][i];
		}
	}
	return v;
}
#endif

void print0x(int x)
{
	std::vector <int> vec;
	while(x)
	{
		vec.push_back(x&1);
		x>>=1;
	}
	std::reverse(vec.begin(),vec.end());
	for(int i=0;i<(int)vec.size();i++)
	{
		std::cout<<vec[i];
	}
	std::cout<<' ';
}

template <typename T>
void print0x(T x,int len)
{
	std::vector <int> vec;
	while(x)
	{
		vec.push_back(x&1);
		x>>=1;
	}
	reverse(vec.begin(),vec.end());
	for(int i=vec.size();i<len;i++)
	{
		putchar('0');
	}
	for(int i=0;i<vec.size();i++)
	{
		std::cout<<vec[i];
	}
	std::cout<<' ';
}
#endif

struct NetworkWithCost
{
	struct Edge
	{
		int vertex;
		int flow;
		int cost;
		int inv;
	};
	vector <Edge> *edges;
	inline void addEdge(int x,int y,int limit,int cost)
	{
		Edge edge;
		edge.cost=cost;
		edge.flow=limit;
		edge.vertex=y;
		edge.inv=(int)edges[y].size();
		edges[x].push_back(edge);
		edge.vertex=x;
		edge.flow=0;
		edge.cost=-cost;
		edge.inv=(int)edges[x].size()-1;
		edges[y].push_back(edge);
	}
	int src,dst;
	int n;
	void init(int _n)
	{
		n=_n;
		edges=new vector <Edge> [n];
		src=dst=-1;
	}
	void feedsd(int _src,int _dst)
	{
		src=_src;
		dst=_dst;
	}
	void clear()
	{
		for(int i=0;i<n;i++)
		{
			edges[i].clear();
		}
		src=dst=-1;
	}
};

namespace MCMFSpace
{
	void operator += (pii &a,const pii &b)
	{
		a.first+=b.first;
		a.second+=b.second;
	}
}

struct MCMF
{
	const int DistInf=1039074182;
	NetworkWithCost g;
	int *dist;
	bool *visit;
	int *cur;
	queue <int> que;
	inline void init(NetworkWithCost &_g)
	{
		g=_g;
		dist=new int[g.n+5];
		visit=new bool[g.n+5];
		cur=new int[g.n+5];
	}
	
	bool bellmanford()
	{
	//	cout<<"Bf started";
		for(int i=0;i<g.n;i++)
		{
			dist[i]=inf;
		}
		dist[g.src]=0;
		int changeCommitted;
		for(int T=0;T<g.n;T++)
		{
			changeCommitted=0;
			for(int i=0;i<g.n;i++)
			{
				if(dist[i]==inf) continue;
				for(auto &e:g.edges[i])
				{
					if(e.flow==0) continue;
					if(dist[e.vertex]>dist[i]+e.cost)
					{
						dist[e.vertex]=dist[i]+e.cost;
						changeCommitted=true;
					}
				}
			}
			if(!changeCommitted) break;
		}
	//	system("pause");
		return (dist[g.dst]<inf);
	}
	
	bool dijkstra()
	{
		for(int i=0;i<g.n;i++)
		{
			dist[i]=-1;
		}
		dist[g.src]=0;
		priority_queue <pii,vector<pii>,greater<pii> > pq;
		pq.push(mp(0,0));
		while(pq.size())
		{
			int x=pq.top().second;
			int cost=pq.top().first;
			pq.pop();
			if(dist[x]!=cost) continue;
			for(auto &e:g.edges[x])
			{
				if(e.flow==0) continue;
				if(dist[e.vertex]>cost+e.cost || dist[e.vertex]==-1)
				{
					dist[e.vertex]=cost+e.cost;
					pq.push(mp(dist[e.vertex],e.vertex));
				}
			}
		}
		return (dist[g.dst]!=-1);
	}
	
	pair<int,int> dfs(int x,int flow)
	{
		if(x==g.dst) return mp(flow,0);
		visit[x]=1;
		pii res={0,0};
		for(int &i=cur[x];i<(int)g.edges[x].size();i++)
		{
			int u=g.edges[x][i].vertex;
			int c=g.edges[x][i].cost;
			if(visit[u]) continue;
			if(dist[u]!=dist[x]+c) continue;
			if(g.edges[x][i].flow==0) continue;
			pii f=dfs(u,min(flow,g.edges[x][i].flow));
			flow-=f.first;
			res+=f;
			res.second+=g.edges[x][i].cost*f.first;
			g.edges[x][i].flow-=f.first;
			g.edges[u][g.edges[x][i].inv].flow+=f.first;
			if(flow==0) break;
		}
		visit[x]=0;
		return res;
	}
	
	pii run()
	{
		pair<int,int> x=mp(0,0);
		for(int i=0;i<g.n;i++)
		{
			visit[i]=0;
		}
		while(bellmanford())
		{
//			cout<<g.n<<endl;
//			for(int i=0;i<g.n;i++)
//			{
//				cout<<dist[i]<<' ';
//			}
//			cout<<endl;
//			Sleep(1000);
			for(int i=0;i<g.n;i++)
			{
				assert(visit[i]==0);
			}
			for(int i=0;i<g.n;i++)
			{
				cur[i]=0;
			}
			pair<int,int> f=dfs(g.src,inf);
//			cout<<"flow="<<f<<endl;
			x+=f;
		}
		return x;
	}
};
using namespace std;
int n,m;
char c[55][55];
const int dx[]={0,1};
const int dy[]={1,0};
int dist[55][55];
int id[55][55];
int rhsNum;
vector <pii> vec[105];

inline bool out(int x,int y)
{
	return (x>=n || y>=m);
}

inline int getID(int x,int y)
{
	return (id[x][y]==-1?id[x][y]=rhsNum++:id[x][y]);
}

inline void bfs(int sx,int sy,int id)
{
	queue <tuple<int,int> > que;
	que.push(make_tuple(sx,sy));
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++)
		{
			dist[i][j]=-inf;
		}
	}
//	memset(dist,-1,sizeof(dist));
	dist[sx][sy]=0;
	while(que.size())
	{
		int x,y;
		tie(x,y)=que.front();
		que.pop();
		for(int i=0;i<2;i++)
		{
			int nx=x+dx[i];
			int ny=y+dy[i];
			if(out(nx,ny) || dist[nx][ny]!=-inf || c[nx][ny]=='#') continue;
			dist[nx][ny]=dist[x][y]+1;
			que.push(make_tuple(nx,ny));
		}
	}
	vector <tuple<int,int,int> > vSort;
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<n;j++)
		{
			vSort.push_back(make_tuple(dist[i][j],i,j));
		}
	}
	sort(all(vSort));
	reverse(all(vSort));
	for(int i=0;i<min(100,(int)vSort.size());i++)
	{
		vec[id].push_back(mp(getID(get<1>(vSort[i]),get<2>(vSort[i])),get<0>(vSort[i])));
	}
}

int main()
{
//	freopen("input.txt","r",stdin);
	memset(id,-1,sizeof(id));
	cin>>n>>m;
	for(int i=0;i<n;i++)
	{
		scanf("%s",c[i]);
	}
	NetworkWithCost g;
	g.init(n*m+2);
	int src=n*m,dst=src+1;
	g.feedsd(src,dst);
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++)
		{
			if(c[i][j]=='#') continue;
			if(!out(i+1,j) && c[i+1][j]!='#')
			{
				g.addEdge(i*m+j,(i+1)*m+j,inf,0);
			}
			if(!out(i,j+1) && c[i][j+1]!='#')
			{
				g.addEdge(i*m+j,(i)*m+(j+1),inf,0);
			}
			if(c[i][j]=='o') g.addEdge(src,i*m+j,1,i+j);
			g.addEdge(i*m+j,dst,1,-i-j);
		}
	}
	MCMF d;
	d.init(g);
	cout<<-d.run().second<<endl;
	return 0;
}


Measured memory_bytes: 4212