#pragma GCC target ("avx2")
#pragma GCC optimize ("unroll-loops")
#pragma GCC optimize ("O3")
#include "bits/stdc++.h"
#include <unordered_set>
#include <unordered_map>
#include <random>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
constexpr ll MOD = 1'000'000'007LL; /*998'244'353LL;*/
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(), (x).end()
#define rep(i, n) for(int (i)=0; (i)<(n); (i)++)
template<class T> bool chmax(T &a, const T &b){ if(a<b){ a=b; return 1; } return 0; }
template<class T> bool chmin(T &a, const T &b){ if(b<a){ a=b; return 1; } return 0; }
constexpr int dy[4]={-1,0,1,0};
constexpr int dx[4]={0,-1,0,1};

struct UnionFind{
private:
    vector<int> par;
    vector<int> siz;
public:
    UnionFind(int N=0){
        init(N);
    }
    void init(int N){
        par.resize(N);
        siz.resize(N);
        for(int i=0; i<N; i++) par[i] = i;
        for(int i=0; i<N; i++) siz[i] = 1;
    }
    void unite(int a, int b){
        int rootA = root(a);
        int rootB = root(b);
        if(rootA == rootB) return;
        if(siz[rootA] < siz[rootB]) swap(rootA, rootB);
        par[rootB] = rootA;
        siz[rootA] += siz[rootB];
    }
    int root(int a){
        if(par[a] == a) return a;
        return par[a] = root(par[a]);
    }
    bool same(int a, int b){
        return root(a) == root(b);
    }
    int size(int a){
        return siz[root(a)];
    }
};

int H, W;
string S[1000];

UnionFind uf;
bool ok[1000][1000];

signed main(){
    cin >> H >> W;
    rep(i, H) cin >> S[i];

    uf.init(H*W);

    rep(i, H) ok[i][0] = ok[i][W-1] = true;
    rep(i, W) ok[0][i] = ok[H-1][i] = true;
    rep(i, H){
        int bef = -1;
        rep(j, W){
            if(S[i][j] == '#'){
                if(bef != -1){
                    int a = uf.root(i*W+j);
                    int b = uf.root(i*W+bef);
                    uf.unite(i*W+j, i*W+bef);
                    int r = uf.root(i*W+j);
                    ok[r/W][r%W] |= ok[a/W][a%W];
                    ok[r/W][r%W] |= ok[b/W][b%W];
                }
                bef = j;
            }
        }
    }
    rep(i, W){
        int bef = -1;
        rep(j, H){
            if(S[j][i] == '#'){
                if(bef != -1){
                    int a = uf.root(j*W+i);
                    int b = uf.root(bef*W+i);
                    uf.unite(j*W+i, bef*W+i);
                    int r = uf.root(j*W+i);
                    ok[r/W][r%W] |= ok[a/W][a%W];
                    ok[r/W][r%W] |= ok[b/W][b%W];
                }
                bef = j;
            }
        }
    }

    int base = 0;
    rep(i, H){
        rep(j, W){
            if(S[i][j] == '#' && uf.root(i*W+j) == i*W+j && !ok[i][j]){
                base++;
            }
        }
    }

    // 横
    int ans1 = 0;
    rep(i, H){
        if(i == 0 || i == H-1) continue;
        bool ex = false;
        rep(j, W){
            if(S[i][j] == '#') ex = true;
        }
        if(!ex) ans1++;
    }

    // 縦
    int ans2 = 0;
    rep(j, W){
        if(j == 0 || j == W-1) continue;
        bool ex = false;
        rep(i, H){
            if(S[i][j] == '#') ex = true;
        }
        if(!ex) ans2++;
    }

    cout << base + min(ans1, ans2) << endl;
}
#include<bits/stdc++.h>
#include<bits/extc++.h>
#pragma GCC optimize("Ofast")
using namespace std;
using namespace __gnu_pbds;
template<typename TH> void _dbg(const char* sdbg, TH h) { cerr<<sdbg<<"="<<h<<"\n"; }
template<typename TH, typename... TA> void _dbg(const char* sdbg, TH h, TA... t){
	while(*sdbg != ',') { cerr<<*sdbg++; } cerr<<"="<<h<<","; _dbg(sdbg+1, t...);
}
#ifdef DEBUG
#define debug(...) _dbg(#__VA_ARGS__, __VA_ARGS__)
#define debugv(x) {{cerr<<#x<<": "; for(auto i:x) cerr<<i<<" "; cerr<<endl;}}
#define debugr(l, r, x) {{cerr<<#x<<": "; for(int i=l;i<=r;i++) cerr<<x<<" "; cerr<<endl;}}
#else
#define debug(...) (__VA_ARGS__)
#define debugv(x)
#define debugr(l, r, x)
#define cerr while(0) cerr
#endif
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
typedef long long ll;
typedef vector<int> vi;
typedef pair<int, int> pii;
#define priority_queue std::priority_queue
#define F first
#define S second
typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> ordered_set;

char a[1005][1005];
bool vis[3005];
vi adj[3005];
void dfs(int x){
	vis[x]=true;
	for(int y:adj[x]){
		if(!vis[y]) dfs(y);
	}
}

signed main(){
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	int h, w;
	cin>>h>>w;
	set<int> row, col;
	auto add=[&](int x, int y){
		adj[x].push_back(h+y);
		adj[h+y].push_back(x);
		row.insert(x);
		col.insert(h+y);
	};
	add(1, 1);
	add(1, w);
	add(h, 1);
	add(h, w);
	for(int i=1;i<=h;i++){
		for(int j=1;j<=w;j++){
			cin>>a[i][j];
			if(a[i][j]=='#'){
				add(i, j);
			}
		}
	}
	int ccs=0;
	for(int i:row){
		if(!vis[i]){
			dfs(i);
			ccs++;
		}
	}
	for(int i:col){
		if(!vis[i]){
			dfs(i);
			ccs++;
		}
	}
	debug(row.size(), col.size());
	cout<<ccs+min(h-row.size(), w-col.size())-1<<endl;
}