Problem:
Patisserie ACM
Amber Claes Maes, a patissier,  opened her own shop last month.
She decided to submit her work to the International Chocolate
Patissier Competition to promote her shop, and she
was pursuing a recipe of sweet chocolate bars.
After thousands of trials, she finally reached the recipe.
However, the recipe required high skill levels to form chocolate to
an orderly rectangular shape.
Sadly, she has just made another strange-shaped chocolate bar as shown in
Figure G-1.
Figure G-1: A strange-shaped chocolate bar
Each chocolate bar consists of many small rectangular segments
of chocolate.  
Adjacent segments are separated with a groove in
between them for ease of snapping.
She planned to cut the strange-shaped chocolate bars into several
rectangular pieces and sell them in her shop.
She wants to cut each chocolate bar as follows.
The bar must be cut along grooves.
The bar must be cut into rectangular pieces.
The bar must be cut into as few pieces as possible.
Following the rules, Figure G-2 can be an instance of cutting of
the chocolate bar shown in Figure G-1.  Figures G-3 and G-4
do not meet the rules; Figure G-3 has
a non-rectangular piece, and Figure G-4 has more pieces
than Figure G-2.
Figure G-2: An instance of cutting that follows the rules
Figure G-3: An instance of cutting that leaves a non-rectangular piece
Figure G-4: An instance of cutting that yields more pieces than Figure G-2
Your job is to write a program that computes the number of pieces
of chocolate after cutting according to the rules.
Input
The input is a sequence of datasets.  The end of the input is indicated by
a line containing two zeros separated by a space.  Each dataset is
formatted as follows.
h
w
r
(1, 1)
...
r
(1,
w
)
r
(2, 1)
...
r
(2,
w
)
...
r
(
h
, 1)
...
r
(
h
,
w
)
The integers
h
and
w
are the lengths of the two orthogonal
dimensions of the chocolate, in number of segments.
You may assume that 2 ≤
h
≤ 100 and 2 ≤
w
≤ 100.
Each of the following
h
lines consists of
w
characters, each is either a
"
.
" or a "
#
".  The character
r
(
i
,
j
)
represents whether 
the chocolate segment exists at the position (
i
,
j
)
as follows.
"
.
": There is no chocolate.
"
#
": There is a segment of chocolate.
You can assume that there is no dataset that represents
either multiple disconnected bars as depicted in Figure G-5
or a bar in a shape with hole(s) as depicted in Figure G-6
and G-7.  You can also assume that there is at least one "
#
"
character in each dataset.
Figure G-5: Disconnected chocolate bars
Figure G-6: A chocolate bar with a hole
Figure G-7: Another instance of a chocolate bar with a hole
Output
For each dataset, output a line containing the integer representing
the number of chocolate pieces obtained by cutting according to the rules.
No other characters are allowed in the output.
Sample Input
3 5
###.#
#####
###..
4 5
.#.##
.####
####.
##.#.
8 8
.#.#.#.#
########
.######.
########
.######.
########
.######.
########
8 8
.#.#.#.#
########
.##.#.#.
##...
...(truncated)

Submitted solutions:

code_snippet: #include <bits/stdc++.h>
using namespace std;
struct Bimatch{
  vector<vector<int>> g;
  vector<int> d, mc, used, vv;
  Bimatch(int n, int m): g(n), mc(m, -1), used(n){
  }
  void add(int u, int v){
    g[u].push_back(v);
  }
  void bfs(){
    d.assign(g.size(), -1);
    queue<int> q;
    for (int i = 0; i < g.size(); i++){
      if (!used[i]){
        q.push(i);
        d[i] = 0;
      }
    }
    while (!q.empty()){
      int x = q.front();
      q.pop();
      for (int e : g[x]){
        int c = mc[e];
        if (c >= 0 && d[c] == -1){
          d[c] = d[x] + 1;
          q.push(c);
        }
      }
    }
  }
  bool dfs(int x){
    vv[x] = true;
    for (int e : g[x]){
      int c = mc[e];
      if (c < 0 || (!vv[c] && d[c] == d[x] + 1 && dfs(c))){
        mc[e] = x;
        used[x] = true;
        return true;
      }
    }
    return false;
  }
  int match(){
    int ret = 0;
    while (true){
      bfs();
      vv.assign(g.size(), false);
      int f = 0;
      for (int i = 0; i < g.size(); i++){
        if (!used[i] && dfs(i)){
          f++;
        }
      }
      if (!f){
        return ret;
      }
      ret += f;
    }
  }
};
int main(){
  while (true){
    int h, w;
    cin >> h >> w;
    if (h == 0 && w == 0){
      break;
    }
    vector<vector<char>> r(h, vector<char>(w));
    for (int i = 0; i < h; i++){
      for (int j = 0; j < w; j++){
        cin >> r[i][j];
      }
    }
    int add = 0;
    for (int i = 0; i < h; i++){
      for (int j = 0; j < w; j++){
        if (r[i][j] == '#'){
          add++;
        }
        if (i < h - 1){
          if (r[i][j] == '#' && r[i + 1][j] == '#'){
            add--;
          }
        }
        if (j < w - 1){
          if (r[i][j] == '#' && r[i][j + 1] == '#'){
            add--;
          }
        }
        if (i < h - 1 && j < w - 1){
          if (r[i][j] == '#' && r[i][j + 1] == '#' && r[i + 1][j] == '#' && r[i + 1][j + 1] == '#'){
            add++;
          }
        }
      }
    }
    int ans = add;
    vector<int> X, Y;
    int V = 0;
    for (int i = 0; i < h - 1; i++){
      for (int j = 0; j < w - 1; j++){
        if ((r[i][j] == '#') + (r[i + 1][j] == '#') + (r[i][j + 1] == '#') + (r[i + 1][j + 1] == '#') == 3){
          X.push_back(i);
          Y.push_back(j);
          V++;
        }
      }
    }
    ans += V;
    vector<vector<int>> sum(h + 1, vector<int>(w + 1, 0));
    for (int i = 0; i < h; i++){
      for (int j = 0; j < w; j++){
        sum[i + 1][j + 1] = (r[i][j] == '.');
      }
    }
    for (int i = 1; i <= h; i++){
      for (int j = 1; j <= w; j++){
        sum[i][j] += sum[i - 1][j];
      }
    }
    for (int i = 1; i <= h; i++){
      for (int j = 1; j <= w; j++){
        sum[i][j] += sum[i][j - 1];
      }
    }
    auto get_sum = [&](int x1, int x2, int y1, int y2){
      return sum[x2][y2] - sum[x2][y1] - sum[x1][y2] + sum[x1][y1];
    };
    vector<vector<int>> idx1(h - 1), idx2(w - 1);
    for (int i = 0; i < V; i++){
      idx1[X[i]].push_back(i);
      idx2[Y[i]].push_back(i);
    }
    vector<pair<int, int>> L, R;
    for (int i = 0; i < h - 1; i++){
      int cnt = idx1[i].size();
      for (int j = 0; j < cnt - 1; j++){
        if (get_sum(i, i + 2, Y[idx1[i][j]] + 1, Y[idx1[i][j + 1]] + 1) == 0){
          L.push_back(make_pair(idx1[i][j], idx1[i][j + 1]));
        }
      }
    }
    for (int i = 0; i < w - 1; i++){
      int cnt = idx2[i].size();
      for (int j = 0; j < cnt - 1; j++){
        if (get_sum(X[idx2[i][j]] + 1, X[idx2[i][j + 1]] + 1, i, i + 2) == 0){
          R.push_back(make_pair(idx2[i][j], idx2[i][j + 1]));
        }
      }
    }
    int cntL = L.size();
    int cntR = R.size();
    Bimatch G(cntL, cntR);
    ans -= cntL + cntR;
    for (int i = 0; i < cntL; i++){
      for (int j = 0; j < cntR; j++){
        if (X[R[j].first] <= X[L[i].first] && X[L[i].first] <= X[R[j].second] && Y[L[i].first] <= Y[R[j].first] && Y[R[j].first] <= Y[L[i].second]){
          G.add(i, j);
        }
      }
    }
    ans += G.match();
    cout << ans << endl;
  }
}
accuracy: 1
time_ms: 30
memory_kb: 3868
final_rank: 2

code_snippet: #include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>

using namespace std;

struct point {
	int x,y;
	point(){}
	point(int a, int b) : x(a), y(b) {}
}P[11111];

struct data {
	int a1,a2,t;
	data(){}
	data(int a, int b, int c) : a1(a), a2(b), t(c) {}
};

char B[111][111];
int a[111][111],b[111][111];
int n,m,sz;

vector<data> H,V;
vector<int> adj[11111];
bool seen[11111];
int R[11111];

bool bpm(int u) {
	for(int i=0;i<adj[u].size();++i) {
		int v = adj[u][i];
		if(seen[v]) continue;
		seen[v] = 1;
		if(R[v]==-1 || bpm(R[v])) {
			R[v] = u;
			return 1;
		}
	} return 0;
}

int match() {
	memset(R,-1,sizeof(R));
	int ret = 0;
	for(int u=0;u<H.size();++u) {
		for(int v=0;v<V.size();++v) seen[v]=0;
		if(bpm(u)) ret++;
	} return ret;
}

int main() {
//#ifdef LOCAL
//	freopen("input.txt","r",stdin);
//#endif
	while(1) {
		scanf("%d%d",&n,&m),sz=0;
		if(n==0&&m==0) break;
		memset(a,0,sizeof(a)),memset(b,0,sizeof(b));
		H.clear(),V.clear();
		for(int i=1;i<=n;++i) scanf("%s",B[i]+1);
		for(int i=1;i<=n;++i) for(int j=1;j<=m;++j)
			a[i][j] = a[i][j-1] + (B[i][j]=='#'),
			b[i][j] = b[i-1][j] + (B[i][j]=='#');
		for(int i=1;i<n;++i) for(int j=1;j<m;++j) {
			int cnt = 0;
			for(int di=0;di<2;++di) for(int dj=0;dj<2;++dj)
				cnt += (B[i+di][j+dj] == '#');
			if(cnt == 3) P[sz++] = point(i,j);
		}

		for(int i=0;i<sz;++i) for(int j=i+1;j<sz;++j) {
			if(P[i].x == P[j].x && a[P[i].x][P[j].y]-a[P[i].x][P[i].y]==P[j].y-P[i].y
				&& a[P[i].x+1][P[j].y]-a[P[i].x+1][P[i].y]==P[j].y-P[i].y)
				H.push_back(data(P[i].y,P[j].y,P[i].x));
			if(P[i].y == P[j].y && b[P[j].x][P[i].y]-b[P[i].x][P[i].y]==P[j].x-P[i].x
				&& b[P[j].x][P[i].y+1]-b[P[i].x][P[i].y+1]==P[j].x-P[i].x)
				V.push_back(data(P[i].x,P[j].x,P[i].y));
		}

		for(int i=0;i<H.size();++i) for(int j=0;j<V.size();++j) {
			int x=H[i].t,y=V[j].t;
			int x1=V[j].a1,x2=V[j].a2;
			int y1=H[i].a1,y2=H[i].a2;
			if(x1<=x&&x<=x2&&y1<=y&&y<=y2) adj[i].push_back(j);
		} printf("%d\n",1+sz-(H.size()+V.size()-match()));

		for(int i=0;i<H.size();++i) adj[i].clear();
	}
}
accuracy: 1
time_ms: 50
memory_kb: 3756
final_rank: 8

code_snippet: #line 1 "library/Template/template.hpp"
#include <bits/stdc++.h>
using namespace std;

#define rep(i, a, b) for (int i = (int)(a); i < (int)(b); i++)
#define rrep(i, a, b) for (int i = (int)(b)-1; i >= (int)(a); i--)
#define ALL(v) (v).begin(), (v).end()
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) (int)v.size()
#define MIN(v) *min_element(ALL(v))
#define MAX(v) *max_element(ALL(v))
#define LB(v, x) int(lower_bound(ALL(v), (x)) - (v).begin())
#define UB(v, x) int(upper_bound(ALL(v), (x)) - (v).begin())

using uint = unsigned int;
using ll = long long int;
using ull = unsigned long long;
using i128 = __int128_t;
using u128 = __uint128_t;
const int inf = 0x3fffffff;
const ll INF = 0x1fffffffffffffff;

template <typename T> inline bool chmax(T &a, T b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T> inline bool chmin(T &a, T b) {
    if (a > b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T, typename U> T ceil(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? (x + y - 1) / y : x / y);
}
template <typename T, typename U> T floor(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? x / y : (x - y + 1) / y);
}
template <typename T> int popcnt(T x) {
    return __builtin_popcountll(x);
}
template <typename T> int topbit(T x) {
    return (x == 0 ? -1 : 63 - __builtin_clzll(x));
}
template <typename T> int lowbit(T x) {
    return (x == 0 ? -1 : __builtin_ctzll(x));
}

template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << "P(" << p.first << ", " << p.second << ")";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec) {
    os << "{";
    for (int i = 0; i < vec.size(); i++) {
        os << vec[i] << (i + 1 == vec.size() ? "" : ", ");
    }
    os << "}";
    return os;
}
template <typename T, typename U>
ostream &operator<<(ostream &os, const map<T, U> &map_var) {
    os << "{";
    for (auto itr = map_var.begin(); itr != map_var.end(); itr++) {
        os << "(" << itr->first << ", " << itr->second << ")";
        itr++;
        if (itr != map_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const set<T> &set_var) {
    os << "{";
    for (auto itr = set_var.begin(); itr != set_var.end(); itr++) {
        os << *itr;
        ++itr;
        if (itr != set_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
#ifdef LOCAL
#define show(...) _show(0, #__VA_ARGS__, __VA_ARGS__)
#else
#define show(...) true
#endif
template <typename T> void _show(int i, T name) {
    cerr << '\n';
}
template <typename T1, typename T2, typename... T3>
void _show(int i, const T1 &a, const T2 &b, const T3 &...c) {
    for (; a[i] != ',' && a[i] != '\0'; i++)
        cerr << a[i];
    cerr << ":" << b << " ";
    _show(i + 1, a, c...);
}
#line 2 "library/Utility/fastio.hpp"
#include <unistd.h>
namespace fastio {
static constexpr uint32_t SZ = 1 << 17;
char ibuf[SZ];
char obuf[SZ];
char out[100];
// pointer of ibuf, obuf

uint32_t pil = 0, pir = 0, por = 0;

struct Pre {
    char num[10000][4];
    constexpr Pre() : num() {
        for (int i = 0; i < 10000; i++) {
            int n = i;
            for (int j = 3; j >= 0; j--) {
                num[i][j] = n % 10 | '0';
                n /= 10;
            }
        }
    }
} constexpr pre;

inline void load() {
    memmove(ibuf, ibuf + pil, pir - pil);
    pir = pir - pil + fread(ibuf + pir - pil, 1, SZ - pir + pil, stdin);
    pil = 0;
    if (pir < SZ)
        ibuf[pir++] = '\n';
}

inline void flush() {
    fwrite(obuf, 1, por, stdout);
    por = 0;
}

void rd(char &c) {
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
}

void rd(string &x) {
    x.clear();
    char c;
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
    do {
        x += c;
        if (pil == pir)
            load();
        c = ibuf[pil++];
    } while (!isspace(c));
}

template <typename T> void rd_real(T &x) {
    string s;
    rd(s);
    x = stod(s);
}

template <typename T> void rd_integer(T &x) {
    if (pil + 100 > pir)
        load();
    char c;
    do
        c = ibuf[pil++];
    while (c < '-');
    bool minus = 0;
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (c == '-') {
            minus = 1, c = ibuf[pil++];
        }
    }
    x = 0;
    while ('0' <= c) {
        x = x * 10 + (c & 15), c = ibuf[pil++];
    }
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (minus)
            x = -x;
    }
}

void rd(int &x) {
    rd_integer(x);
}
void rd(ll &x) {
    rd_integer(x);
}
void rd(i128 &x) {
    rd_integer(x);
}
void rd(uint &x) {
    rd_integer(x);
}
void rd(ull &x) {
    rd_integer(x);
}
void rd(u128 &x) {
    rd_integer(x);
}
void rd(double &x) {
    rd_real(x);
}
void rd(long double &x) {
    rd_real(x);
}

template <class T, class U> void rd(pair<T, U> &p) {
    return rd(p.first), rd(p.second);
}
template <size_t N = 0, typename T> void rd_tuple(T &t) {
    if constexpr (N < std::tuple_size<T>::value) {
        auto &x = std::get<N>(t);
        rd(x);
        rd_tuple<N + 1>(t);
    }
}
template <class... T> void rd(tuple<T...> &tpl) {
    rd_tuple(tpl);
}

template <size_t N = 0, typename T> void rd(array<T, N> &x) {
    for (auto &d : x)
        rd(d);
}
template <class T> void rd(vector<T> &x) {
    for (auto &d : x)
        rd(d);
}

void read() {}
template <class H, class... T> void read(H &h, T &...t) {
    rd(h), read(t...);
}

void wt(const char c) {
    if (por == SZ)
        flush();
    obuf[por++] = c;
}
void wt(const string s) {
    for (char c : s)
        wt(c);
}
void wt(const char *s) {
    size_t len = strlen(s);
    for (size_t i = 0; i < len; i++)
        wt(s[i]);
}

template <typename T> void wt_integer(T x) {
    if (por > SZ - 100)
        flush();
    if (x < 0) {
        obuf[por++] = '-', x = -x;
    }
    int outi;
    for (outi = 96; x >= 10000; outi -= 4) {
        memcpy(out + outi, pre.num[x % 10000], 4);
        x /= 10000;
    }
    if (x >= 1000) {
        memcpy(obuf + por, pre.num[x], 4);
        por += 4;
    } else if (x >= 100) {
        memcpy(obuf + por, pre.num[x] + 1, 3);
        por += 3;
    } else if (x >= 10) {
        int q = (x * 103) >> 10;
        obuf[por] = q | '0';
        obuf[por + 1] = (x - q * 10) | '0';
        por += 2;
    } else
        obuf[por++] = x | '0';
    memcpy(obuf + por, out + outi + 4, 96 - outi);
    por += 96 - outi;
}

template <typename T> void wt_real(T x) {
    ostringstream oss;
    oss << fixed << setprecision(15) << double(x);
    string s = oss.str();
    wt(s);
}

void wt(int x) {
    wt_integer(x);
}
void wt(ll x) {
    wt_integer(x);
}
void wt(i128 x) {
    wt_integer(x);
}
void wt(uint x) {
    wt_integer(x);
}
void wt(ull x) {
    wt_integer(x);
}
void wt(u128 x) {
    wt_integer(x);
}
void wt(double x) {
    wt_real(x);
}
void wt(long double x) {
    wt_real(x);
}

template <class T, class U> void wt(const pair<T, U> val) {
    wt(val.first);
    wt(' ');
    wt(val.second);
}
template <size_t N = 0, typename T> void wt_tuple(const T t) {
    if constexpr (N < std::tuple_size<T>::value) {
        if constexpr (N > 0) {
            wt(' ');
        }
        const auto x = std::get<N>(t);
        wt(x);
        wt_tuple<N + 1>(t);
    }
}
template <class... T> void wt(tuple<T...> tpl) {
    wt_tuple(tpl);
}
template <class T, size_t S> void wt(const array<T, S> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}
template <class T> void wt(const vector<T> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}

void print() {
    wt('\n');
}
template <class Head, class... Tail> void print(Head &&head, Tail &&...tail) {
    wt(head);
    if (sizeof...(Tail))
        wt(' ');
    print(forward<Tail>(tail)...);
}
void __attribute__((destructor)) _d() {
    flush();
}
} // namespace fastio

using fastio::flush;
using fastio::print;
using fastio::read;

inline void first(bool i = true) {
    print(i ? "first" : "second");
}
inline void Alice(bool i = true) {
    print(i ? "Alice" : "Bob");
}
inline void Takahashi(bool i = true) {
    print(i ? "Takahashi" : "Aoki");
}
inline void yes(bool i = true) {
    print(i ? "yes" : "no");
}
inline void Yes(bool i = true) {
    print(i ? "Yes" : "No");
}
inline void No() {
    print("No");
}
inline void YES(bool i = true) {
    print(i ? "YES" : "NO");
}
inline void NO() {
    print("NO");
}
inline void Yay(bool i = true) {
    print(i ? "Yay!" : ":(");
}
inline void Possible(bool i = true) {
    print(i ? "Possible" : "Impossible");
}
inline void POSSIBLE(bool i = true) {
    print(i ? "POSSIBLE" : "IMPOSSIBLE");
}

/**
 * @brief Fast IO
 */
#line 3 "sol.cpp"

#line 2 "library/Graph/maxflow.hpp"

struct MaxFlow {
    struct Edge {
        int to, rev;
        ll cap;
    };
    int V;
    vector<vector<Edge>> G;
    vector<int> itr, level;
    using P = pair<int, int>;
    vector<P> es;

  public:
    MaxFlow() {}
    MaxFlow(int V) : V(V) {
        G.assign(V, vector<Edge>());
    }
    int add_vertex() {
        G.push_back(vector<Edge>());
        return V++;
    }
    void add_edge(int from, int to, ll cap) {
        int fid = SZ(G[from]), tid = SZ(G[to]);
        if (from == to)
            tid++;
        es.push_back({from, fid});
        G[from].push_back({to, tid, cap});
        G[to].push_back({from, fid, 0});
    }
    struct Type {
        int from, to;
        ll cap, recap;
    };
    Type get_edge(int i) {
        auto [from, pos] = es[i];
        auto e = G[from][pos];
        auto re = G[e.to][e.rev];
        return Type{from, e.to, e.cap, re.cap};
    }
    void bfs(int s) {
        level.assign(V, -1);
        queue<int> q;
        level[s] = 0;
        q.push(s);
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            for (auto &e : G[v]) {
                if (e.cap > 0 && level[e.to] < 0) {
                    level[e.to] = level[v] + 1;
                    q.push(e.to);
                }
            }
        }
    }
    ll dfs(int v, int t, ll f) {
        if (v == t)
            return f;
        for (int &i = itr[v]; i < (int)G[v].size(); i++) {
            Edge &e = G[v][i];
            if (e.cap > 0 && level[v] < level[e.to]) {
                ll d = dfs(e.to, t, min(f, e.cap));
                if (d > 0) {
                    e.cap -= d, G[e.to][e.rev].cap += d;
                    return d;
                }
            }
        }
        return 0;
    }
    ll run(int s, int t) {
        ll ret = 0, f;
        while (bfs(s), level[t] >= 0) {
            itr.assign(V, 0);
            while ((f = dfs(s, t, INF)) > 0)
                ret += f;
        }
        return ret;
    }
    vector<int> cut() {
        vector<int> ret(V);
        rep(v, 0, V) if (level[v] < 0) ret[v] = 1;
        return ret;
    }
};

/**
 * @brief Maximum Flow
 */
#line 5 "sol.cpp"

int main() {
    for (;;) {
        int H, W;
        read(H, W);
        if (H == 0 and W == 0)
            break;
        vector<string> a(H);
        read(a);

        using P = pair<int, int>;
        using Q = pair<P, int>;
        vector<Q> vs;
        int ret = 1;
        rep(i, 0, H - 1) rep(j, 0, W - 1) {
            int cnt = 0;
            rep(k, 0, 2) rep(l, 0, 2) if (a[i + k][j + l] == '#') cnt++;
            if (cnt == 3) {
                ret++;
                rep(k, 0, 2) rep(l, 0, 2) if (a[i + k][j + l] == '.') {
                    if (k == 0) {
                        vs.push_back({{i, j}, 0});
                    }
                    if (l == 0) {
                        vs.push_back({{i, j}, 1});
                    }
                }
            }
        }

        int n = SZ(vs);
        MaxFlow mf(n + 2);
        int S = n, T = n + 1;

        // line, profit
        vector<int> L(n);
        rep(i, 0, n) {
            auto [xy1, dir1] = vs[i];
            auto [x1, y1] = xy1;
            if (dir1 == 0) {
                L[i] = 1;
                for (;;) {
                    int x = x1 + L[i], y = y1;
                    if (x >= H - 1)
                        break;
                    if (a[x + 1][y] == '.' or a[x + 1][y + 1] == '.')
                        break;
                    L[i]++;
                }
                if (x1 + L[i] < H - 1 and (a[x1 + L[i] + 1][y1] == '#' or
                                           a[x1 + L[i] + 1][y1 + 1] == '#')) {
                    ret--;
                    mf.add_edge(i, T, 1);
                }
            } else {
                L[i] = 1;
                for (;;) {
                    int x = x1, y = y1 + L[i];
                    if (y >= W - 1)
                        break;
                    if (a[x][y + 1] == '.' or a[x + 1][y + 1] == '.')
                        break;
                    L[i]++;
                }
                if (y1 + L[i] < W - 1 and (a[x1][y1 + L[i] + 1] == '#' or
                                           a[x1 + 1][y1 + L[i] + 1] == '#')) {
                    ret--;
                    mf.add_edge(S, i, 1);
                }
            }
        }

        // cross, touch
        rep(i, 0, n) {
            auto [xy1, dir1] = vs[i];
            auto [x1, y1] = xy1;
            if (dir1 == 0) {
                rep(j, 0, n) {
                    auto [xy2, dir2] = vs[j];
                    auto [x2, y2] = xy2;
                    if (dir2 == 1) {
                        if (x1 < x2 and x2 < x1 + L[i] and y2 < y1 and
                            y1 < y2 + L[j]) {
                            mf.add_edge(j, i, inf);
                            continue;
                        }
                        bool touch = 0;
                        if (x1 == x2 and y1 == y2)
                            touch = 1;
                        if (x1 + L[i] == x2 and y1 == y2)
                            touch = 1;
                        if (x1 == x2 and y1 == y2 + L[j])
                            touch = 1;
                        if (x1 + L[i] == x2 and y1 == y2 + L[j])
                            touch = 1;
                        if (touch) {
                            mf.add_edge(j, i, 1);
                        }
                    }
                }
            }
        }

        show(ret);
        ret += mf.run(S, T);
        print(ret);
    }
    return 0;
}
accuracy: 1
time_ms: 70
memory_kb: 4460
final_rank: 10

code_snippet: #include <complex>
#include <iomanip>
#include <iostream>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <stack>
#include <unordered_map>
#include <unordered_set>
using namespace std;

#define rep(i, n) for (int i = 0; i < (n); i++)
#define per(i, n) for (int i = (n)-1; i >= 0; i--)
#define rep2(i, l, r) for (int i = (l); i < (r); i++)
#define per2(i, l, r) for (int i = (r)-1; i >= (l); i--)
#define each(e, x) for (auto &e : x)
#define sz(x) (int)x.size()
#define all(x) begin(x), end(x)
#define rall(x) rbegin(x), rend(x)
#define eb emplace_back
#define MM << ' ' <<
#define TT template <typename T>
using ll = long long;
using pii = pair<int, int>;
using pll = pair<ll, ll>;

TT bool chmin(T &x, T y) { return x > y ? (x = y, true) : false; }
TT bool chmax(T &x, T y) { return x < y ? (x = y, true) : false; }

TT void print(vector<T> v, T x = 0) {
    int n = sz(v);
    rep(i, n) cout << v[i] + x << (i == n - 1 ? '\n' : ' ');
    if (n == 0) cout << '\n';
}

TT void printn(vector<T> v, T x = 0) {
    int n = sz(v);
    rep(i, n) cout << v[i] + x << '\n';
}

TT void rearrange(vector<T> &v) {
    sort(all(v));
    v.erase(unique(all(v)), end(v));
}

TT int lb(vector<T> &v, T x) { return lower_bound(all(v), x) - begin(v); }
TT int ub(vector<T> &v, T x) { return upper_bound(all(v), x) - begin(v); }

const int inf = (1 << 30) - 1;
const ll INF = (1LL << 60) - 1;

struct io_setup {
    io_setup() {
        ios_base::sync_with_stdio(false);
        cin.tie(NULL);
        cout << fixed << setprecision(15);
    }
} io_setup;

struct BipartiteMatching {
    vector<vector<int>> es;
    vector<int> d, match;
    vector<bool> used, used2;
    int n, m;

    BipartiteMatching(int n, int m) : es(n), d(n), match(m), used(n), used2(n), n(n), m(m) {}

    void add_edge(int u, int v) { es[u].eb(v); }

    void bfs() {
        fill(all(d), -1);
        queue<int> que;
        rep(i, n) {
            if (!used[i]) {
                que.emplace(i);
                d[i] = 0;
            }
        }
        while (!empty(que)) {
            int i = que.front();
            que.pop();
            each(e, es[i]) {
                int j = match[e];
                if (j != -1 && d[j] == -1) {
                    que.emplace(j);
                    d[j] = d[i] + 1;
                }
            }
        }
    }

    bool dfs(int i) {
        used2[i] = true;
        each(e, es[i]) {
            int j = match[e];
            if (j == -1 || (!used2[j] && d[j] == d[i] + 1 && dfs(j))) {
                match[e] = i;
                used[i] = true;
                return true;
            }
        }
        return false;
    }

    int solve() {
        fill(all(match), -1);
        fill(all(used), false);
        int ret = 0;
        while (true) {
            bfs();
            fill(all(used2), false);
            int f = 0;
            rep(i, n) {
                if (!used[i] && dfs(i)) f++;
            }
            if (f == 0) break;
            ret += f;
        }
        return ret;
    }
};

int main() {
    while (true) {
        int H, W;
        cin >> H >> W;

        if (H == 0) break;

        vector<string> S(H);
        rep(i, H) cin >> S[i];

        auto count = [&](int i, int j) {
            int ret = 0;
            rep2(k, i, i + 2) rep2(l, j, j + 2) {
                if (S[k][l] == '#') ret++;
            }
            return ret;
        };

        vector<tuple<int, int, int>> yoko, tate;

        rep(i, H - 1) {
            int pre = -1;
            rep(j, W - 1) {
                if (count(i, j) == 3) {
                    if (pre != -1) {
                        if (S[i][pre + 1] == '#' && S[i + 1][pre + 1] == '#') {
                            bool flag = true;
                            rep2(k, pre + 1, j) {
                                if (count(i, k) != 4) flag = false;
                            }
                            if (flag) yoko.eb(i, pre, j);
                        }
                    }
                    pre = j;
                }
            }
        }

        rep(j, W - 1) {
            int pre = -1;
            rep(i, H - 1) {
                if (count(i, j) == 3) {
                    if (pre != -1) {
                        if (S[pre + 1][j] == '#' && S[pre + 1][j + 1] == '#') {
                            bool flag = true;
                            rep2(k, pre + 1, i) {
                                if (count(k, j) != 4) flag = false;
                            }
                            if (flag) tate.eb(j, pre, i);
                        }
                    }
                    pre = i;
                }
            }
        }

        auto intersect = [](tuple<int, int, int> yoko, tuple<int, int, int> tate) {
            auto [x, ly, ry] = yoko;
            auto [y, lx, rx] = tate;
            return (lx <= x && x <= rx && ly <= y && y <= ry);
        };

        int N = sz(yoko), M = sz(tate);

        BipartiteMatching G(N, M);
        rep(i, N) rep(j, M) {
            if (intersect(yoko[i], tate[j])) G.add_edge(i, j);
        }

        int pointNum = 0;
        rep(i, H - 1) rep(j, W - 1) {
            if (count(i, j) == 3) pointNum++;
        }

        int maxMatching = G.solve();
        int maxIndependentSet = N + M - maxMatching;

        cout << pointNum - maxIndependentSet + 1 << '\n';
    }
}
accuracy: 1
time_ms: 20
memory_kb: 3744
final_rank: 1

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define rep(i, n) for (int i = 0; i < int(n); i++)
#define per(i, n) for (int i = (n)-1; 0 <= i; i--)
#define rep2(i, l, r) for (int i = (l); i < int(r); i++)
#define per2(i, l, r) for (int i = (r)-1; int(l) <= i; i--)
#define MM << " " <<
#define pb push_back
#define eb emplace_back
#define all(x) begin(x), end(x)
#define rall(x) rbegin(x), rend(x)
// #define sz(x) (int)x.size()
#define re(x) real(x)
#define im(x) imag(x)

template<typename T>
void print(const vector<T> &v) {
    int n = v.size();
    for (int i = 0; i < n; i++) cout << v[i] << (i == n - 1 ? '\n' : ' ');
    if (v.empty()) cout << '\n';
}

template<class T>
using MaxHeap = priority_queue<T>;
template<class T>
using MinHeap = priority_queue<T, vector<T>, greater<T>>;

using ll  = long long;
using pii = pair<int, int>;
using pll = pair<ll, ll>;

template<typename T>
bool chmax(T &x, const T &y) {
    return (x < y) ? (x = y, true) : false;
}

template<typename T>
bool chmin(T &x, const T &y) {
    return (x > y) ? (x = y, true) : false;
}

int h, w;
int dx[] = {0, -1, -1, 0};
int dy[] = {0, 0, -1, -1};

int sgn(int x) {
    return x == 0 ? 0 : (x < 0) ? -1 : 1; 
}

pair<int, set<pii>> sil(vector<string> a) {
    map<pii, vector<pii>> st;
    rep(i, h + 2) rep(j, w + 1) {
        if(a[i][j] != a[i][j + 1]) {
            pii s = {i, j + 1};
            pii t = {i + 1, j + 1};
            st[s].emplace_back(t);
            st[t].emplace_back(s);
        }
    }
    rep(i, h + 1) rep(j, w + 2) {
        if(a[i][j] != a[i + 1][j]) {
            pii s = {i + 1, j};
            pii t = {i + 1, j + 1};
            st[s].emplace_back(t);
            st[t].emplace_back(s);
        }
    }
    auto judge = [a](pii i) {
        auto [x, y] = i;
        int c = 0;
        for(auto &j : {0, -1}) for(auto &k : {0, -1}) {
            c += (a[x + j][y + k] == '#') ? 1 : 0;
        }
        return c == 3;
    };
    auto now = begin(st)->first;
    set<pii> used;
    vector<pii> crr;
    while(!used.count(now)) {
        used.insert(now);
        if(judge(now)) {
            crr.push_back(now);
        }
        for(auto &i : st[now]) {
            if(!used.count(i)) {
                now = i;
                break;
            }
        }
    }
    set<pii> res;
    map<pii, int> pp;
    rep(i, crr.size()) pp[crr[i]] = i;
    rep(i, crr.size()) {
        rep(j, 4) {
            auto [x, y] = crr[i];
            int s = j;
            int t = (j + 1) % 4;
            while(a[x + dx[s]][y + dy[s]] == '#' && a[x + dx[t]][y + dy[t]] == '#') {
                x += dx[s] + dx[t] + 1;
                y += dy[s] + dy[t] + 1;
            }
            if(pp.count({x, y})) {
                int tmp = pp[{x, y}];
                if(tmp < i) res.insert({tmp, i});
            }
        }
    }
    return {crr.size(), res};
}

int solve() {
    vector<string> a(h + 2);
    rep(i, h) cin >> a[i + 1];
    rep2(i, 1, h + 1) {
        a[i] = "." + a[i] + ".";
    }
    rep(i, w + 2) {
        a[0] += ".";
        a[h + 1] += ".";
    }
    auto [n, p] = sil(a);
    // return n;
    vector<vector<int>> jud(n + 1, vector<int>());
    for(auto &i : p) {
        // jud[i.first].push_back(i.second);
        jud[i.second].push_back(i.first);
    }
    vector<vector<int>> dp(n + 1, vector<int>(n + 1, n * 3));
    per(i, n + 1) {
        dp[i][i] = 0;
        rep2(j, i + 1, n + 1) {
            for(auto &k : jud[j - 1]) {
                if(i <= k) chmin(dp[i][j], dp[i][k] + dp[k + 1][j - 1] + 1);
            }
            chmin(dp[i][j], dp[i][j - 1] + 1);
            chmin(dp[i][j], dp[i + 1][j] + 1);
            // if(2 <= j - i) {
            //     if(jud[i][j - 1]) {
            //         chmin(dp[i][j], dp[i + 1][j - 1] + 1);
            //     }
            // }
        }
    }
    return dp[0][n] + 1;
}

int main() {
    ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    while(1) {
        cin >> h >> w;
        if(h == 0) break;
        cout << solve() << endl;
    }
}
accuracy: 1
time_ms: 310
memory_kb: 97968
final_rank: 20

code_snippet: #include <algorithm>
#include <bitset>
#include <cassert>
#include <cmath>
#include <complex>
#include <iomanip>
#include <iostream>
#include <limits>
#include <map>
#include <numeric>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>

#define rep(i, s, n) for (int i = (int)(s); i < (int)(n); i++)
#define rrep(i, s, n) for (int i = (int)(n)-1; i >= (int)(s); i--)
#define all(v) v.begin(), v.end()

using ll = long long;
using ld = long double;
using ull = unsigned long long;

template <typename T> bool chmin(T &a, const T &b) {
    if (a <= b) return false;
    a = b;
    return true;
}
template <typename T> bool chmax(T &a, const T &b) {
    if (a >= b) return false;
    a = b;
    return true;
}
using namespace std;

template <typename fa> struct FordFulkerson {
	struct edge{
		int to; fa cap; int rev; bool isrev;
	};
	const fa INF;
	vector<vector<edge>> g;
	vector<int> used;
	int ts;
	explicit FordFulkerson(int v) : INF(numeric_limits<fa>::max()),g(v),used(v,-1),ts(0){}
	void add_edge(int from, int to, fa cap){
		g[from].push_back((edge){to,cap,(int)g[to].size(),false});
		g[to].push_back((edge){from,0,(int)g[from].size()-1,true});
	}
	fa dfs(int v, int t, fa flow){
		if(v==t)return flow;
		used[v] = ts;
		for (auto &e: g[v]){
			if (e.cap>0&&used[e.to]!=ts){
				fa d=dfs(e.to,t,min(flow,e.cap));
				if(d>0){
					e.cap-=d;
					g[e.to][e.rev].cap+=d;
					return d;
				}
			}
		}
		return 0;
	}
	fa flow(int s, int t){
		fa ret = 0;
		for (fa f; (f = dfs(s,t,INF))>0;ts++) ret+=f;
		ts++;
		return ret;
	}
};


void solve(int h, int w){
	vector c(h, vector<char>(w));
	rep(i,0,h) rep(j,0,w) cin >> c[i][j];
	
	vector<pair<int,int>> points;
	rep(i,0,h-1) rep(j,0,w-1){
		int cnt = 0;
		rep(x,0,2) rep(y,0,2){
			if (c[i+x][j+y] == '#') cnt++;
		}
		if (cnt == 3){
			points.push_back(pair(i, j));
		}
	}

	int m = points.size();
	// taio
	vector<pair<int,int>> sets1;
	vector<pair<int,int>> sets2;
	rep(i,0,m){
		rep(j,i+1,m){
			if (points[i].first == points[j].first){
				int a = points[i].second;
				int b = points[j].second;
				int t = points[i].first;
				if (a > b) swap(a, b);
				bool mode = true;
				rep(x,a+1,b+1){
					if (c[t][x] == '.' || c[t+1][x] == '.'){
						mode = false;
						break;
					}
				}
				if (mode){
					sets1.push_back(pair(i,j));
				}
			}
			if (points[i].second == points[j].second){
				int a = points[i].first;
				int b = points[j].first;
				int t = points[i].second;
				if (a > b) swap(a, b);
				bool mode = true;
				rep(x,a+1,b+1){
					if (c[x][t] == '.' || c[x][t+1] == '.'){
						mode = false;
						break;
					}
				}
				if (mode){
					sets2.push_back(pair(i,j));
				}
			}
		}
	}

	int n1 = sets1.size();
	int n2 = sets2.size();
	//cout << n1 << " " << n2 << " " << m << endl;

	FordFulkerson<int> mf(n1+n2+2);
	rep(i,0,n1){
		rep(j,0,n2){
			set<int> r;
			int xt1 = points[sets1[i].first].second;
			int xt2 = points[sets1[i].second].second;
			int yv = points[sets1[i].first].first;

			if (xt1 > xt2) swap(xt1,xt2);

			int yt1 = points[sets2[j].first].first;
			int yt2 = points[sets2[j].second].first;
			int xv = points[sets2[j].first].second;

			if (yt1 > yt2) swap(yt1,yt2);

			if ((yt1 <= yv && yv <= yt2 && xt1 <= xv && xv <= xt2)){
				mf.add_edge(i, j+n1, 1);
			}		
		}
	}
	rep(i,0,n1){
		mf.add_edge(n1+n2, i, 1);
	}
	rep(j,0,n2){
		mf.add_edge(j+n1, n1+n2+1, 1);
	}
	//cout << m << " " << n1 << " " << n2 << " " << " " << mf.flow(n1+n2, n1+n2+1) << endl;
	cout << m - n1 - n2 + mf.flow(n1+n2, n1+n2+1) + 1 << endl;
}

int main(){
	while (true){
		int h, w; cin >> h >> w;
		if (h == 0 && w == 0) break;
		solve(h, w);
	}
}
accuracy: 1
time_ms: 130
memory_kb: 3996
final_rank: 14

code_snippet: #include "bits/stdc++.h"
using namespace std;
using namespace std::chrono;
typedef long long int llint;
typedef long double lldo;
#define mp make_pair
#define mt make_tuple
#define pub push_back
#define puf push_front
#define pob pop_back
#define pof pop_front
#define fir first
#define sec second
#define res resize
#define ins insert
#define era erase
#define REP(i, n) for(int i = 0;i < (n);i++)
/*cout<<fixed<<setprecision(20);cin.tie(0);ios::sync_with_stdio(false);*/
const int mod=1000000007;
const llint inf=2.19e7+1;
const long double pai=3.141592653589793238462643383279502884197;
const long double eps=1e-10;
template <class T,class U>bool chmin(T& a,U b){if(a>b){a=b;return true;}return false;}
template <class T,class U>bool chmax(T& a,U b){if(a<b){a=b;return true;}return false;}
llint gcd(llint a,llint b){if(a%b==0){return b;}else return gcd(b,a%b);}
llint lcm(llint a,llint b){if(a==0){return b;}return a/gcd(a,b)*b;}
template<class T> void SO(T& ve){sort(ve.begin(),ve.end());}
template<class T> void REV(T& ve){reverse(ve.begin(),ve.end());}
template<class T>int LBI(const vector<T>&ar,T in){return lower_bound(ar.begin(),ar.end(),in)-ar.begin();}
template<class T>int UBI(const vector<T>&ar,T in){return upper_bound(ar.begin(),ar.end(),in)-ar.begin();}

template<class T>using V=vector<T>;
template<class T>using VV=vector<vector<T>>;
struct E{int to,rev,cap;};

template<class C>
struct Maxflow{
	C flow;
	V<char>dual;
};

template<class C>
struct MFExec{
	VV<E>& g;
	int s,t;
	V<int>level,iter;
	C dfs(int v,C f){
		if(v==t){return f;}
		C res=0;
		for(int& i=iter[v];i<int(g[v].size());i++){
			E& e=g[v][i];
			if(e.cap<=0||level[v]>=level[e.to]){continue;}
			C  d=dfs(e.to,min(f,e.cap));
			e.cap-=d;
			g[e.to][e.rev].cap+=d;
			res+=d;
			f-=d;
			if(f==0){break;}
		}
		return res;
	}
	Maxflow<C>info;
	MFExec(VV<E>&_g,int _s,int _t):g(_g),s(_s),t(_t){
		int N=int(g.size());
		
		info.flow=0;
		C& flow=info.flow;
		while(1){
			queue<int>que;
			level=V<int>(N,-1);
			level[s]=0;
			que.push(s);
			while(!que.empty()){
				int v=que.front();
				que.pop();
				for(E e:g[v]){
					if(e.cap<=0||level[e.to]>=0){continue;}
					level[e.to]=level[v]+1;
					que.push(e.to);
				}
			}
			if(level[t]==-1){break;}
			iter=V<int>(N,0);
			while(1){
				C f=dfs(s,inf);
				if(!f){break;}
				flow+=f;
			}
		}
	}
};

void add_edge(VV<E>&in,int A,int B,int C){
	in[A].push_back(E{B,int(in[B].size()),C});
	in[B].push_back(E{A,int(in[A].size())-1,0});
}
int main(void){
	/*
	A,B 異なるときにコスト
	*/
	while(-1){
		//ACM 洋菓子店 つくりかけ フローで解くべき
		int h,w,i,j;cin>>h>>w;
		if(h==0){break;}
		vector<string>mat(h+2);
		mat[0]=string(w+2,'.');
		mat[h+1]=string(w+2,'.');
		for(i=1;i<=h;i++){
			cin>>mat[i];
			mat[i].insert(mat[i].begin(),'.');
			mat[i].pub('.');
		}
		VV<E> gra((h+1)*(w+1)*2+2);
		//横、縦の順
		int S=(h+1)*(w+1)*2,T=(h+1)*(w+1)*2+1;
		int ans=0;
		for(i=0;i<=h;i++){
			for(j=0;j<=w;j++){
				int X=(i*(w+1)+j)*2;
				if(mat[i][j+1]=='#'&&mat[i+1][j+1]=='#'){//次と連結する
					add_edge(gra,X,X+2,2);
					add_edge(gra,X+2,X,2);
				}
				if(mat[i+1][j]=='#'&&mat[i+1][j+1]=='#'){//次と連結する
					add_edge(gra,X+1,X+1+(w+1)*2,2);
					add_edge(gra,X+1+(w+1)*2,X+1,2);
				}
				if(mat[i][j]=='.'&&mat[i+1][j]=='.'){
					add_edge(gra,X,T,2);
				}
				if(mat[i][j+1]=='.'&&mat[i+1][j+1]=='.'){
					add_edge(gra,X,T,2);
				}
				
				if(mat[i][j]=='.'&&mat[i][j+1]=='.'){
					add_edge(gra,S,X+1,2);
				}
				if(mat[i+1][j]=='.'&&mat[i+1][j+1]=='.'){
					add_edge(gra,S,X+1,2);
				}
				
				add_edge(gra,X,X+1,10);//両方はダメ
				
				int kaz=0;
				if(mat[i][j]=='#'){kaz++;}
				if(mat[i][j+1]=='#'){kaz++;}
				if(mat[i+1][j]=='#'){kaz++;}
				if(mat[i+1][j+1]=='#'){kaz++;}
				if(kaz==3){add_edge(gra,X+1,X,10);}//この角には必ずつける必要がある
				ans+=kaz%2;
			}
		}
		MFExec<int> aaa=MFExec<int>(gra,S,T);
		cout<<(ans+aaa.info.flow)/4<<endl;
	}
	return 0;
}
/*
.#.##
.####
####.
##.#.


*/
accuracy: 1
time_ms: 370
memory_kb: 6704
final_rank: 17

code_snippet: #include <iostream>
#include <vector>
#include <tuple>
#include <algorithm>
using namespace std;

struct Edge {
	int to, cap, rev;
};

class MaxFlow {
public:
	int size_ = 0;
	vector<Edge> G[10009];
	bool used[10009];

	void init(int sz) {
		size_ = sz;
		for (int i = 0; i < 10009; i++) G[i].clear();
	}

	void add_edge(int u, int v, int f) {
		G[u].push_back(Edge{ v, f, (int)G[v].size() });
		G[v].push_back(Edge{ u, 0, (int)G[u].size() - 1 });
	}

	int dfs(int pos, int goal, int f) {
		if (pos == goal) return f;
		used[pos] = true;

		// 辺を探索する
		for (int i = 0; i < G[pos].size(); i++) {
			if (used[G[pos][i].to] == true || G[pos][i].cap == 0) continue;
			int F = dfs(G[pos][i].to, goal, min(f, G[pos][i].cap));
			if (F != 0) {
				G[pos][i].cap -= F;
				G[G[pos][i].to][G[pos][i].rev].cap += F;
				return F;
			}
		}

		// 見つからなかった場合
		return 0;
	}

	int max_flow(int s, int t) {
		int FLOW = 0;
		while (true) {
			for (int i = 0; i < size_; i++) used[i] = false;
			int f = dfs(s, t, 1000000007);
			if (f == 0) break;
			FLOW += f;
		}
		return FLOW;
	}
};

// 入力
int H, W;
char C[1009][1009];

// フローなど
MaxFlow Z;
vector<tuple<int, int, int>> V1; // 縦
vector<tuple<int, int, int>> V2; // 横
int idx[1009][1009];

int solve() {
	// 初期化
	V1.clear();
	V2.clear();
	for (int i = 0; i < 109; i++) {
		for (int j = 0; j < 109; j++) idx[i][j] = -1;
	}

	// 270 度を結ぶ線を探す（縦）
	for (int i = 1; i <= H - 1; i++) {
		vector<int> vec;
		vec.push_back(0);
		for (int j = 1; j <= W; j++) {
			int cnt = 0;
			if (C[i + 0][j] == '#') cnt += 1;
			if (C[i + 1][j] == '#') cnt += 1;
			vec.push_back(cnt);
		}
		vec.push_back(0);

		// 線を探す
		int Start = -1;
		for (int j = 0; j < vec.size(); j++) {
			if (vec[j] == 2 && Start == -1) Start = j;
			if (vec[j] != 2 && Start != -1) {
				if (vec[Start - 1] == 1 && vec[j] == 1) {
					V1.push_back(make_tuple(i, Start - 1, j - 1));
				}
				Start = -1;
			}
		}
	}

	// 270 度を結ぶ線を探す（横）
	for (int j = 1; j <= W - 1; j++) {
		vector<int> vec;
		vec.push_back(0);
		for (int i = 1; i <= H; i++) {
			int cnt = 0;
			if (C[i][j + 0] == '#') cnt += 1;
			if (C[i][j + 1] == '#') cnt += 1;
			vec.push_back(cnt);
		}
		vec.push_back(0);

		// 線を探す
		int Start = -1;
		for (int i = 0; i < vec.size(); i++) {
			if (vec[i] == 2 && Start == -1) Start = i;
			if (vec[i] != 2 && Start != -1) {
				if (vec[Start - 1] == 1 && vec[i] == 1) {
					V2.push_back(make_tuple(j, Start - 1, i - 1));
				}
				Start = -1;
			}
		}
	}

	// マッチングを求める（準備）
	int L1 = V1.size();
	int L2 = V2.size();
	Z.init(L1 + L2 + 3);
	for (int i = 0; i < L2; i++) {
		for (int j = get<1>(V2[i]); j <= get<2>(V2[i]); j++) idx[j][get<0>(V2[i])] = i;
	}
	for (int i = 0; i < L1; i++) Z.add_edge(L1 + L2 + 0, i, 1);
	for (int i = 0; i < L2; i++) Z.add_edge(L1 + i, L1 + L2 + 1, 1);

	// マッチングを求める
	for (int i = 0; i < L1; i++) {
		for (int j = get<1>(V1[i]); j <= get<2>(V1[i]); j++) {
			int val = idx[get<0>(V1[i])][j];
			if (val != -1) {
				Z.add_edge(i, L1 + val, 1);
			}
		}
	}

	// 最大フロー
	int Answer1 = Z.max_flow(L1 + L2 + 0, L1 + L2 + 1);
	int Answer2 = L1 + L2 - Answer1;

	// 答えを返す
	int Count270 = 0;
	for (int i = 1; i <= H - 1; i++) {
		for (int j = 1; j <= W - 1; j++) {
			int cnt = 0;
			if (C[i + 0][j + 0] == '#') cnt++;
			if (C[i + 0][j + 1] == '#') cnt++;
			if (C[i + 1][j + 0] == '#') cnt++;
			if (C[i + 1][j + 1] == '#') cnt++;
			if (cnt == 3) Count270 += 1;
		}
	}
	return Count270 - Answer2 + 1;
}

int main() {
	while (true) {
		// 入力
		cin >> H >> W; if (H == 0 && W == 0) break;
		for (int i = 1; i <= H; i++) {
			for (int j = 1; j <= W; j++) cin >> C[i][j];
		}
		
		// 出力
		int Answer = solve();
		cout << Answer << endl;
	}
	return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 7592
final_rank: 9

code_snippet: #include<iostream>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
#include<functional>
#include<iomanip>
#include<queue>
#include<ciso646>
#include<random>
#include<map>
#include<set>
#include<bitset>
#include<stack>
#include<unordered_map>
#include<utility>
#include<cassert>
#include<complex>
#include<numeric>
using namespace std;

//#define int long long
typedef long long ll;

typedef unsigned long long ul;
typedef unsigned int ui;
constexpr ll mod = 1000000007;
const ll INF = mod * mod;
typedef pair<int, int>P;
#define stop char nyaa;cin>>nyaa;
#define rep(i,n) for(int i=0;i<n;i++)
#define per(i,n) for(int i=n-1;i>=0;i--)
#define Rep(i,sta,n) for(int i=sta;i<n;i++)
#define rep1(i,n) for(int i=1;i<=n;i++)
#define per1(i,n) for(int i=n;i>=1;i--)
#define Rep1(i,sta,n) for(int i=sta;i<=n;i++)
#define all(v) (v).begin(),(v).end()
typedef pair<ll, ll> LP;
typedef double ld;
typedef pair<ld, ld> LDP;
const ld eps = 1e-12;
const ld pi = acos(-1.0);

ll mod_pow(ll x, ll n, ll m = mod) {
	ll res = 1;
	while (n) {
		if (n & 1)res = res * x % m;
		x = x * x % m; n >>= 1;
	}
	return res;
}
struct modint {
	ll n;
	modint() :n(0) { ; }
	modint(ll m) :n(m) {
		if (n >= mod)n %= mod;
		else if (n < 0)n = (n % mod + mod) % mod;
	}
	operator int() { return n; }
};
bool operator==(modint a, modint b) { return a.n == b.n; }
modint operator+=(modint& a, modint b) { a.n += b.n; if (a.n >= mod)a.n -= mod; return a; }
modint operator-=(modint& a, modint b) { a.n -= b.n; if (a.n < 0)a.n += mod; return a; }
modint operator*=(modint& a, modint b) { a.n = ((ll)a.n * b.n) % mod; return a; }
modint operator+(modint a, modint b) { return a += b; }
modint operator-(modint a, modint b) { return a -= b; }
modint operator*(modint a, modint b) { return a *= b; }
modint operator^(modint a, ll n) {
	if (n == 0)return modint(1);
	modint res = (a * a) ^ (n / 2);
	if (n % 2)res = res * a;
	return res;
}

ll inv(ll a, ll p) {
	return (a == 1 ? 1 : (1 - p * inv(p % a, a)) / a + p);
}
modint operator/(modint a, modint b) { return a * modint(inv(b, mod)); }

const int max_n = 1 << 20;
modint fact[max_n], factinv[max_n];
void init_f() {
	fact[0] = modint(1);
	for (int i = 0; i < max_n - 1; i++) {
		fact[i + 1] = fact[i] * modint(i + 1);
	}
	factinv[max_n - 1] = modint(1) / fact[max_n - 1];
	for (int i = max_n - 2; i >= 0; i--) {
		factinv[i] = factinv[i + 1] * modint(i + 1);
	}
}
modint comb(int a, int b) {
	if (a < 0 || b < 0 || a < b)return 0;
	return fact[a] * factinv[b] * factinv[a - b];
}

ll gcd(ll a, ll b) {
	if (a < b)swap(a, b);
	while (b) {
		ll r = a % b; a = b; b = r;
	}
	return a;
}

//x,yがax+by=gcd(a,b)の解になる
ll extgcd(ll a, ll b, ll& x, ll& y) {
	ll d = a;
	if (b != 0) {
		d = extgcd(b, a % b, y, x);
		y -= (a / b) * x;
	}
	else {
		x = 1; y = 0;
	}
	return d;
}
//aのmod mでの逆元を求める
ll mod_inverse(ll a, ll m) {
	ll x, y;
	extgcd(a, m, x, y);
	return (m + x % m) % m;
}

#include<cstring>
struct edge { int to, cap, rev; };
vector<edge> G[100000];
bool used[100000];
void add_edge(int from, int to, int cap) {
	//cout << from << " " << to << " " << cap << "\n";
	G[from].push_back(edge{ to, cap, (int)G[to].size() });
	G[to].push_back(edge{ from, 0, (int)G[from].size() - 1 });
}
int dfs(int v, int t, int f) {
	if (v == t)return f;
	used[v] = true;
	for (int i = 0; i < (int)G[v].size(); i++) {
		edge& e = G[v][i];
		if (!used[e.to] && e.cap > 0) {
			int d = dfs(e.to, t, min(f, e.cap));
			if (d > 0) {
				e.cap -= d;
				G[e.to][e.rev].cap += d;
				return d;
			}
		}
	}
	return 0;
}
int max_flow(int s, int t) {
	int flow = 0;
	for (;;) {
		memset(used, 0, sizeof(used));
		int f = dfs(s, t, mod);
		if (f == 0)return flow;
		flow += f;
	}
}

int h, w;
void solve() {
	vector<string> s(h);
	rep(i, h)cin >> s[i];
	map<pair<P, P>, int> xt, yt;
	vector<P> ps;
	rep(i, h - 1)rep(j, w - 1) {
		int cnt = 0;
		rep(x, 2)rep(y, 2)if (s[i + x][j + y] == '#')cnt++;
		if (cnt == 3) {
			int d = 0;
			rep(x, 2)rep(y, 2)if (s[i + x][j + y] != '#')d = 2 * x + y;
			int tx, ty;
			if (d % 2 == 0) {
				int cur = j + 1;
				while (cur < w && s[i][cur] == '#' && s[i + 1][cur] == '#')cur++;
				cur--;
				pair<P, P> p = { {i,j},{i,cur} };
				if (xt.find(p) == xt.end()) {
					xt[p] = xt.size();
				}
				tx = xt[p];
			}
			else {
				int cur = j;
				while (cur >= 0 && s[i][cur] == '#' && s[i + 1][cur] == '#')cur--;
				pair<P, P> p = { {i,cur},{i,j} };
				if (xt.find(p) == xt.end())xt[p] = xt.size();
				tx = xt[p];
			}
			if (d < 2) {
				int cur = i + 1;
				while (cur < h && s[cur][j] == '#' && s[cur][j + 1] == '#')cur++;
				cur--;
				pair<P, P> p = { {i,j},{cur,j} };
				if (yt.find(p) == yt.end())yt[p] = yt.size();
				ty = yt[p];
			}
			else {
				int cur = i;
				while (cur >= 0 && s[cur][j] == '#' && s[cur][j + 1] == '#')cur--;
				pair<P, P> p = { {cur,j},{i,j} };
				if (yt.find(p) == yt.end())yt[p] = yt.size();
				ty = yt[p];
			}
			ps.push_back({ tx,ty });
		}
	}
	int ss = xt.size() + yt.size();
	int tt = ss + 1;
	rep(i, tt + 1)G[i].clear();
	rep(i, xt.size()) {
		add_edge(ss, i, 1);
	}
	rep(i, yt.size()) {
		add_edge(i + xt.size(), tt, 1);
	}
	for (P p : ps) {
		add_edge(p.first, p.second + xt.size(), 1);
	}
	for (auto p1 : xt)for (auto p2 : yt) {
		pair<P, P> xp = p1.first, yp = p2.first;
		int zy = yp.first.second;
		bool valid = true;
		if (zy <= xp.first.second || xp.second.second <= zy)valid = false;
		int zx = xp.first.first;
		if (zx <= yp.first.first || yp.second.first <= zx)valid = false;
		if (valid) {
			add_edge(p2.second + xt.size(), p1.second, 1);
		}
	}
	int ans = max_flow(ss, tt); ans++;
	cout << ans << "\n";
}





signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	//cout << fixed << setprecision(10);
	//init_f();
	//init();
	//expr();
	//int t; cin >> t; rep(i, t)
	while (cin >> h >> w,h) {
		solve();
	}
	return 0;
}
accuracy: 1
time_ms: 290
memory_kb: 24932
final_rank: 18

code_snippet: #include <bits/stdc++.h>

using namespace std;

template<typename flow_t>
struct Dinic{
    const flow_t INF;
    struct edge{
        int to;
        flow_t cap;
        int rev;
        bool isrev;
        int idx;
    };
    vector<vector<edge>>graph;
    vector<int>min_cost,iter;
    Dinic(int V):INF(numeric_limits<flow_t>::max()),graph(V){};
    void add_edge(int from,int to,flow_t cap,int idx=-1){
        // cout << "  " << "from " << from << ", to " << to << ", cap " << cap << endl;
        graph[from].push_back({to,cap,(int)graph[to].size(),false,idx});
        graph[to].push_back({from,0,(int)graph[from].size()-1,true,idx});

    }
    bool bfs(int s,int t){
        min_cost.assign(graph.size(),-1);
        queue<int>que;
        min_cost[s]=0;
        que.push(s);
        while(!que.empty()&&min_cost[t]==-1){
            int p = que.front();
            que.pop();
            for(auto &e:graph[p]){
                if(e.cap>0&&min_cost[e.to]==-1){
                    min_cost[e.to]=min_cost[p]+1;
                    que.push(e.to);
                }
            }
        }
        return min_cost[t]!=-1;
    }
    flow_t dfs(int idx,const int t, flow_t flow){
        if(idx==t)return flow;
        for(int &i=iter[idx];i<graph[idx].size();i++){
            edge &e = graph[idx][i];
            if(e.cap>0&&min_cost[idx]<min_cost[e.to]){
                flow_t d = dfs(e.to,t,min(flow,e.cap));
                if(d>0){
                    e.cap-=d;
                    graph[e.to][e.rev].cap+=d;
                    return d;
                }
            }
        }
        return 0;
    }
    flow_t max_flow(int s,int t){
        flow_t flow=0;
        while(bfs(s,t)){
            iter.assign(graph.size(),0);
            flow_t f = 0;
            while((f=dfs(s,t,INF))>0)flow+=f;

        }
        return flow;
    }
};

int h, w;
vector<string> choco;

bool input(void){
    cin >> h >> w;
    choco.resize(h);
    for (string& si : choco)
        cin >> si;
    return !(h == 0 && w == 0);
}

bool isNG(int x, int y){
    int cntChoco = 0;
    for (int dx = 0; dx <= 1; ++dx)
        for (int dy = 0; dy <= 1; ++dy)
            if (choco[x + dx][y + dy] == '#')
                ++cntChoco;
    return (cntChoco == 3);
}

bool canCut(int x1, int y1, int x2, int y2){
    if (y1 == y2){
        bool ret = true;
        for (int x = x1 + 1; x <= x2; ++x)
            ret = ret && (choco[x][y1] == '#' && choco[x][y1 + 1] == '#');
        return ret;
    }
    else if (x1 == x2){
        bool ret = true;
        for (int y = y1 + 1; y <= y2; ++y)
            ret = ret && (choco[x1][y] == '#' && choco[x1 + 1][y] == '#');
        return ret;
    }
    return false;
}

int NG[100][100];
int constructNG(void){
    for (int i = 0; i < h - 1; ++i){
        for (int j = 0; j < w - 1; ++j){
            if (isNG(i, j)) NG[i][j] = 0;
            else NG[i][j] = -1;
        }
    }
    int tmp = 0;
    for (int i = 0; i < h - 1; ++i){
        for (int j = 0; j < w - 1; ++j){
            if (NG[i][j] >= 0)
                NG[i][j] = tmp++; // NGな点の頂点番号
        }
    }
    return tmp; // 頂点数
}

struct Line{
    int x1, y1, x2, y2;
    Line() {}
    Line(int x1, int y1, int x2, int y2)
     : x1(x1), y1(y1), x2(x2), y2(y2) {}
};

// tate : y固定
// yoko : x固定
bool cross(Line& tate, Line& yoko){
    return (
        yoko.y1 <= tate.y1 && tate.y1 <= yoko.y2 &&
        tate.x1 <= yoko.x1 && tate.x2 >= yoko.x1
    );
}

vector<vector<int>> biG;

int solve(void){
    int nNG = constructNG();
    if (nNG == 0) return 1;
    vector<Line> tate, yoko;
    for (int x = 0; x < h - 1; ++x){ // x固定でyを動かす
        vector<int> y_idx;
        for (int y = 0; y < w - 1; ++y)
            if (NG[x][y] >= 0)
                y_idx.push_back(y);
        for (int y1 : y_idx){
            for (int y2 : y_idx){
                if (y1 < y2){
                    bool ok = canCut(x, y1, x, y2);
                    if (ok){
                        yoko.push_back(Line(x, y1, x, y2));
                    }
                }
            }
        }
    }
    for (int y = 0; y < w - 1; ++y){ // y固定でxを動かす
        vector<int> x_idx;
        for (int x = 0; x < h - 1; ++x)
            if (NG[x][y] >= 0)
                x_idx.push_back(x);
        for (int x1 : x_idx){
            for (int x2 : x_idx){
                if (x1 < x2){
                    bool ok = canCut(x1, y, x2, y);
                    if (ok){
                        tate.push_back(Line(x1, y, x2, y));
                    }
                }
            }
        }
    }
    int line = tate.size() + yoko.size();
    int t = tate.size(), y = yoko.size();
    Dinic<int> d(line + 2);
    for (int i = 0; i < t; ++i)
        d.add_edge(0, i + 1, 1);
    for (int i = 0; i < y; ++i)
        d.add_edge(t + 1 + i, t + y + 1, 1);
    for (int i = 0; i < t; ++i){
        for (int j = 0; j < y; ++j){
            bool c = cross(tate[i], yoko[j]);
            if (c){
                d.add_edge(i + 1, t + 1 + j, 1);
            }
        }
    }
    int fl = d.max_flow(0, t + y + 1);
    int mis = line - fl;
    /* cout << "line : " << line << endl;
    cout << "mis : " << mis << endl;
    cout << "nNG : " << nNG << endl; */
    return (nNG  - mis + 1);
}

int main(void){ 
    while (input()){
        cout << solve() << endl;
    }
    return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 4536
final_rank: 4

code_snippet: #include<bits/stdc++.h>
using namespace std;
using Int = long long;
template<typename T1,typename T2> inline void chmin(T1 &a,T2 b){if(a>b) a=b;}
template<typename T1,typename T2> inline void chmax(T1 &a,T2 b){if(a<b) a=b;}


struct BiMatch{
  int n,time;
  vector<vector<int>> G;
  vector<int> match,used,dead;

  BiMatch(){}
  BiMatch(int n):n(n),time(0),G(n),
                 match(n,-1),used(n,-1),dead(n,0){}

  void add_edge(int u,int v){
    G[u].emplace_back(v);
    G[v].emplace_back(u);
  }

  int dfs(int v){
    used[v]=time;
    for(int u:G[v]){
      if(dead[u]) continue;
      int w=match[u];
      if((w<0)||(used[w]<time&&dfs(w))){
        match[v]=u;
        match[u]=v;
        return 1;
      }
    }
    return 0;
  }

  int build(){
    int res=0;
    for(int v=0;v<n;v++){
      if(dead[v]) continue;
      if(match[v]<0){
        time++;
        res+=dfs(v);
      }
    }
    return res;
  }

  int disable(int v){
    assert(!dead[v]);
    int u=match[v];
    if(~u) match[u]=-1;
    match[v]=-1;
    dead[v]=1;
    time++;
    return ~u?dfs(u)-1:0;
  }

  int enable(int v){
    assert(dead[v]);
    dead[v]=0;
    time++;
    return dfs(v);
  }
};

//INSERT ABOVE HERE
signed main(){
  int h,w;
  while(cin>>h>>w,h){
    vector<string> st(h);
    for(int i=0;i<h;i++) cin>>st[i];
    using P = pair<int, int>;
    vector<P> vp;
    map<P, int> idx;

    vector<vector<int>> tp(h,vector<int>(w,-1));
    for(int i=0;i+1<h;i++){
      for(int j=0;j+1<w;j++){
        int num=0;
        for(int a=0;a<2;a++)
          for(int b=0;b<2;b++)
            num+=st[i+a][j+b]=='#';

        if(num==3){
          if(st[i+0][j+0]=='.') tp[i][j]=0;
          if(st[i+0][j+1]=='.') tp[i][j]=1;
          if(st[i+1][j+0]=='.') tp[i][j]=2;
          if(st[i+1][j+1]=='.') tp[i][j]=3;

          idx[P(i,j)]=vp.size();
          vp.emplace_back(i,j);
        }
      }
    }

    int n=vp.size();
    using P = pair<int, int>;
    vector<P> es;

    for(int i=0;i+1<h;i++){
      for(int j=0;j+1<w;j++){
        if(tp[i][j]<0) continue;
        if(tp[i][j]<2){
          for(int k=i+1;k+1<h;k++){
            if(tp[k][j]<0){
              if(st[k][j+0]=='#'&&st[k][j+1]=='#') continue;
              break;
            }
            if(tp[k][j]>=2)
              es.emplace_back(idx[P(i,j)],idx[P(k,j)]);
            break;
          }
        }
        if(tp[i][j]%2==0){
          for(int l=j+1;l+1<w;l++){
            if(tp[i][l]<0){
              if(st[i+0][l]=='#'&&st[i+1][l]=='#') continue;
              break;
            }
            if(tp[i][l]%2==1)
              es.emplace_back(idx[P(i,j)],idx[P(i,l)]);
            break;
          }
        }
      }
    }

    #define F first
    #define S second
    int m=es.size();
    BiMatch G(m);
    for(int i=0;i<m;i++){
      for(int j=0;j<m;j++){
        if(vp[es[i].F].F!=vp[es[i].S].F) continue;
        if(vp[es[j].F].S!=vp[es[j].S].S) continue;
        if(vp[es[j].F].S< vp[es[i].F].S) continue;
        if(vp[es[j].F].S> vp[es[i].S].S) continue;
        if(vp[es[i].F].F< vp[es[j].F].F) continue;
        if(vp[es[i].F].F> vp[es[j].S].F) continue;
        G.add_edge(i,j);
        // cout<<i<<" "<<j<<endl;
      }
    }

    cout<<n-(m-G.build())+1<<endl;
  }
  return 0;
}
accuracy: 1
time_ms: 170
memory_kb: 3700
final_rank: 15

code_snippet: #include <bits/stdc++.h>
using namespace std;

using ll=long long;
#define int ll

#define rng(i,a,b) for(int i=int(a);i<int(b);i++)
#define rep(i,b) rng(i,0,b)
#define gnr(i,a,b) for(int i=int(b)-1;i>=int(a);i--)
#define per(i,b) gnr(i,0,b)
#define pb push_back
#define eb emplace_back
#define a first
#define b second
#define bg begin()
#define ed end()
#define all(x) x.bg,x.ed
#ifdef LOCAL
#define dmp(x) cerr<<__LINE__<<" "<<#x<<" "<<x<<endl
#else
#define dmp(x) void(0)
#endif

template<class t,class u> void chmax(t&a,u b){if(a<b)a=b;}
template<class t,class u> void chmin(t&a,u b){if(b<a)a=b;}

template<class t> using vc=vector<t>;
template<class t> using vvc=vc<vc<t>>;

using pi=pair<int,int>;
using vi=vc<int>;

template<class t,class u>
ostream& operator<<(ostream& os,const pair<t,u>& p){
	return os<<"{"<<p.a<<","<<p.b<<"}";
}

template<class t> ostream& operator<<(ostream& os,const vc<t>& v){
	os<<"{";
	for(auto e:v)os<<e<<",";
	return os<<"}";
}

#define mp make_pair
#define mt make_tuple
#define one(x) memset(x,-1,sizeof(x))
#define zero(x) memset(x,0,sizeof(x))
#ifdef LOCAL
void dmpr(ostream&os){os<<endl;}
template<class T,class... Args>
void dmpr(ostream&os,const T&t,const Args&... args){
	os<<t<<" ";
	dmpr(os,args...);
}
#define dmp2(...) dmpr(cerr,"Line:",__LINE__,##__VA_ARGS__)
#else
#define dmp2(...) void(0)
#endif

using uint=unsigned;
using ull=unsigned long long;

template<int i,class T>
void print_tuple(ostream&,const T&){
}

template<int i,class T,class H,class ...Args>
void print_tuple(ostream&os,const T&t){
	if(i)os<<",";
	os<<get<i>(t);
	print_tuple<i+1,T,Args...>(os,t);
}

template<class ...Args>
ostream& operator<<(ostream&os,const tuple<Args...>&t){
	os<<"{";
	print_tuple<0,tuple<Args...>,Args...>(os,t);
	return os<<"}";
}

void print(ll x,int suc=1){
	cout<<x;
	if(suc==1)
		cout<<"\n";
	if(suc==2)
		cout<<" ";
}

ll read(){
	ll i;
	cin>>i;
	return i;
}

vi readvi(int n,int off=0){
	vi v(n);
	rep(i,n)v[i]=read()+off;
	return v;
}

template<class T>
void print(const vector<T>&v,int suc=1){
	rep(i,v.size())
		print(v[i],i==int(v.size())-1?suc:2);
}

string readString(){
	string s;
	cin>>s;
	return s;
}

template<class T>
T sq(const T& t){
	return t*t;
}

//#define CAPITAL
void yes(bool ex=true){
	#ifdef CAPITAL
	cout<<"YES"<<endl;
	#else
	cout<<"Yes"<<endl;
	#endif
	if(ex)exit(0);
}
void no(bool ex=true){
	#ifdef CAPITAL
	cout<<"NO"<<endl;
	#else
	cout<<"No"<<endl;
	#endif
	if(ex)exit(0);
}

constexpr ll ten(int n){
	return n==0?1:ten(n-1)*10;
}

const ll infLL=LLONG_MAX/3;

#ifdef int
const int inf=infLL;
#else
const int inf=INT_MAX/2-100;
#endif

int topbit(signed t){
	return t==0?-1:31-__builtin_clz(t);
}
int topbit(ll t){
	return t==0?-1:63-__builtin_clzll(t);
}
int botbit(signed a){
	return a==0?32:__builtin_ctz(a);
}
int botbit(ll a){
	return a==0?64:__builtin_ctzll(a);
}
int popcount(signed t){
	return __builtin_popcount(t);
}
int popcount(ll t){
	return __builtin_popcountll(t);
}
bool ispow2(int i){
	return i&&(i&-i)==i;
}
int mask(int i){
	return (int(1)<<i)-1;
}

bool inc(int a,int b,int c){
	return a<=b&&b<=c;
}

template<class t> void mkuni(vc<t>&v){
	sort(all(v));
	v.erase(unique(all(v)),v.ed);
}

ll rand_int(ll l, ll r) { //[l, r]
	#ifdef LOCAL
	static mt19937_64 gen;
	#else
    static random_device rd;
    static mt19937_64 gen(rd());
    #endif
    return uniform_int_distribution<ll>(l, r)(gen);
}

template<class t>
int lwb(const vc<t>&v,const t&a){
	return lower_bound(all(v),a)-v.bg;
}

template<class d>
struct maxflow{
	struct E{
		int to,rev;
		d cap;
	};
	vvc<E> g;
	vi itr,lv;
	maxflow(int n):g(n),itr(n),lv(n){}
	void ae(int s,int t,d c){
		g[s].pb({t,(int)g[t].size(),c});
		g[t].pb({s,(int)g[s].size()-1,0});
	}
	void bfs(int s){
		fill(all(lv),-1);
		lv[s]=0;
		queue<int> q;
		q.push(s);
		while(q.size()){
			int v=q.front();q.pop();
			for(auto e:g[v])if(e.cap>0&&lv[e.to]==-1){
				lv[e.to]=lv[v]+1;
				q.push(e.to);
			}
		}
	}
	d dfs(int v,int t,d f){
		if(v==t)return f;
		d res=0;
		for(int&i=itr[v];i<(int)g[v].size();i++){
			E& e=g[v][i];
			if(e.cap>0&&lv[e.to]==lv[v]+1){
				d a=dfs(e.to,t,min(f,e.cap));
				if(a>0){
					e.cap-=a;
					g[e.to][e.rev].cap+=a;
					res+=a;
					f-=a;
					if(f<=0)break;
				}
			}
		}
		return res;
	}
	d calc(int s,int t){
		d f=0;
		while(1){
			bfs(s);
			if(lv[t]==-1)
				return f;
			fill(all(itr),0);
			f+=dfs(s,t,inf);
		}
	}
};

signed main(){
	cin.tie(0);
	ios::sync_with_stdio(0);
	cout<<fixed<<setprecision(20);
	
	while(1){
		int h,w;cin>>h>>w;
		if(h==0)break;
		
		vvc<int> a(h+2);
		a[0]=vi(w+2,0);
		rng(i,1,h+1){
			a[i].pb(0);
			string s;cin>>s;
			for(auto c:s)
				a[i].pb(c=='#');
			a[i].pb(0);
		}
		a[h+1]=vi(w+2,0);
		h+=2;
		w+=2;
		
		vc<tuple<int,int,int>> ls[2];
		rep(z,2){
			rep(i,h-1)rep(j,w-1){
				if(a[i+1][j]&&a[i+1][j+1]&&a[i][j]+a[i][j+1]==1){
					int k=i+1;
					while(a[k][j]+a[k][j+1]+a[k+1][j]+a[k+1][j+1]==4)k++;
					if(a[k+1][j]+a[k+1][j+1]==1){
						ls[z].eb(j+1,i+1,k+1);
					}
				}
			}
			vvc<int> b(w,vi(h));
			rep(i,h)rep(j,w)
				b[j][i]=a[i][j];
			swap(a,b);
			swap(h,w);
		}
		
		int cnt=0;
		rep(i,h-1)rep(j,w-1)if(a[i][j]+a[i][j+1]+a[i+1][j]+a[i+1][j+1]==3)
			cnt++;
		
		int x=ls[0].size(),y=ls[1].size();
		
		maxflow<int> mf(1+x+y+1);
		rep(i,x)mf.ae(0,1+i,1);
		rep(i,y)mf.ae(1+x+i,1+x+y,1);
		rep(i,x)rep(j,y){
			auto p=ls[0][i];
			auto q=ls[1][j];
			bool ok=true;
			rep(_,2){
				if(!inc(get<1>(q),get<0>(p),get<2>(q)))
					ok=false;
				swap(p,q);
			}
			if(ok){
				mf.ae(1+i,1+x+j,1);
			}
		}
		
		int f=mf.calc(0,1+x+y);
		
		cout<<cnt-(x+y-f)+1<<endl;
	}
}
accuracy: 1
time_ms: 40
memory_kb: 3976
final_rank: 5

code_snippet: #include <bits/stdc++.h>
using namespace std;

#define REP(i,n) for(int i=0; i<n; i++)
#define FOR(i,a,b) for(int i=a; i<b; i++)
#define FORR(i,a,b) for(int i=(int)b-1; i>=a; i--)

#define ALL(v) (v).begin(), (v).end()

using ll = long long;
using pii = pair<int,int>;
using pll = pair<ll,ll>;
using vi = vector<int>;
using vl = vector<ll>;

#define DEBUG(x) cerr<<#x<<": "<<(x)<<endl
#define CHMIN(a,b) (a)=min((a),(b))
#define CHMAX(a,b) (a)=max((a),(b))

const int INF = 1ll<<28;
using flow_type = int;

struct dinic{
	struct edge{
		int from, to;
		flow_type cost;
		int rev;
	};
	int n;
	vector<vector<edge>> G,C;
	vector<int> level;
	vector<bool> finished;
	dinic(int n):n(n){
		G.assign(n,vector<edge>());
		level.assign(n,0);
		finished.assign(n,false);
	}
	void add_edge(int from,int to,flow_type cost){
		edge s = (edge){from,to,cost,(int)G[to].size()};
		edge t = (edge){to,from,(flow_type)0,(int)G[from].size()};
		G[from].push_back(s);
		G[to].push_back(t);
	}
	flow_type dfs(int u, int t, flow_type cur){
		if(u==t || cur==0)return cur;
		if(finished[u])return 0;
		finished[u] = true;
		for(edge &e : C[u]){
			if(level[e.to] > level[u]){
				flow_type f = dfs(e.to, t, min(cur, e.cost));
				if(f>0){
					e.cost -= f;
					C[e.to][e.rev].cost += f;
					finished[u] = false;
					return f;
				}
			}
		}
		return 0;
	}
	flow_type calc(int s, int t){
		C = G;
		flow_type total = 0;
		while(true){
			REP(i,n)level[i] = -1;
			level[s] = 0;
			queue<int> Q; Q.push(s);
			int d = n;
			while(!Q.empty()){
				int u = Q.front(); Q.pop();
				for(edge &e:C[u]){
					if(e.cost > 0 && level[e.to] == -1){
						Q.push(e.to);
						level[e.to] = level[u] + 1;
					}
				}
			}
			REP(i,n)finished[i] = false;
			bool flag = false;
			while(true){
				flow_type f = dfs(s,t,INF);
				if(f==0)break;
				total += f;
				flag = true;
			}
			if(!flag)break;
		}
		return total;
	}
};

int w,h;
char mp[125][125];

bool vert(pair<pii,pii> line){
	return line.first.second == line.second.second;
}

int main(){
	while(true){
		scanf("%d%d",&h,&w);
		if(h+w==0)break;
		REP(i,125)REP(j,125)mp[i][j] = '.';
		REP(i,h)scanf("%s",mp[i+1]+1);
		h+=2; w+=2;
		vector<pair<pii,pii>> lines;
		vector<pii> vertices;
		FOR(y1,1,h-1)FOR(x1,1,w-1){
			int cnt = 0;
			REP(dy,2)REP(dx,2)cnt += (mp[y1-dy][x1-dx]=='#');
			if(cnt!=3)continue;
			vertices.push_back(pii(y1,x1));
			{
				int cy=y1, cx=x1;
				while(mp[cy][cx]=='#' && mp[cy-1][cx]=='#')cx++;
				int cnt = 0;
				REP(dy,2)REP(dx,2)cnt += (mp[cy-dy][cx-dx]=='#');
				if((cy!=y1 || cx!=x1) && cnt==3){
					lines.push_back(make_pair(pii(y1,x1),pii(cy,cx)));
					// printf("%d %d - %d %d\n",y1,x1,cy,cx);
				}
			}
			{
				int cy=y1, cx=x1;
				while(mp[cy][cx]=='#' && mp[cy][cx-1]=='#')cy++;
				int cnt = 0;
				REP(dy,2)REP(dx,2)cnt += (mp[cy-dy][cx-dx]=='#');
				if((cy!=y1 || cx!=x1) && cnt==3){
					lines.push_back(make_pair(pii(y1,x1),pii(cy,cx)));
					// printf("%d %d - %d %d\n",y1,x1,cy,cx);
				}
			}
		}
		int N = lines.size();
		const int S = N;
		const int T = N+1;
		dinic D(N+2);
		REP(i,N){
			if(vert(lines[i])){
				D.add_edge(S,i,+1);
				D.add_edge(i,T,+0);
			}else{
				D.add_edge(S,i,+0);
				D.add_edge(i,T,+1);
			}
		}
		REP(i,N)REP(j,i){
			pair<pii,pii> a = lines[i], b = lines[j];
			if(vert(a)==vert(b))continue;
			bool flag = false;
			if(vert(b))flag=true,swap(a,b);
			assert(vert(a));
			if(b.second.second < a.first.second)continue;
			if(a.first.second < b.first.second)continue;
			if(a.second.first < b.first.first)continue;
			if(b.first.first < a.first.first)continue;
			if(!flag){
				// i is vert
				D.add_edge(i,j,+40000);
			}else{
				D.add_edge(j,i,+40000);
			}
			// DEBUG(i); DEBUG(j);
		}
		int unused = D.calc(S,T);
		int used = N - unused;
		int ans = used + (vertices.size() - 2*used) + 1;
		printf("%d\n",ans);
		// break;
	}
	return 0;
}
accuracy: 1
time_ms: 120
memory_kb: 4516
final_rank: 13

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define REP(i,n) for(ll (i) = (0);(i) < (n);++i)
#define REV(i,n) for(ll (i) = (n) - 1;(i) >= 0;--i)
#define PB push_back
#define EB emplace_back
#define MP make_pair
#define FI first
#define SE second
#define SHOW1d(v,n) {REP(WW,n)cerr << v[WW] << ' ';cerr << endl << endl;}
#define SHOW2d(v,WW,HH) {REP(W_,WW){REP(H_,HH)cerr << v[W_][H_] << ' ';cerr << endl;}cerr << endl;}
#define ALL(v) v.begin(),v.end()
#define Decimal fixed<<setprecision(20)
#define INF 1000000000
#define LLINF 1000000000000000000LL
#define MOD 998244353

typedef long long ll;
typedef pair<ll,ll> P;

struct Dinic {
	struct edge{int to;long long cap,rev;};
	vector<vector<edge>> G;
	vector<int> level;
	vector<int> iter;

	Dinic(int n):G(n, vector<edge>()),level(n),iter(n){}

	void add_edge(int from,int to,long long cap){
		G[from].push_back((edge){to,cap,(ll)G[to].size()});
		G[to].push_back((edge){from,0,(ll)G[from].size()-1});
	}

	void bfs(int s){
		fill(level.begin(), level.end(), -1);
		queue<int> que;
		level[s] = 0;
		que.push(s);
		while(!que.empty())	{
			int v = que.front();que.pop();
			for(int i = 0;i < G[v].size();++i){
				edge &e = G[v][i];
				if(e.cap > 0 && level[e.to] < 0){
					level[e.to] = level[v] + 1;
					que.push(e.to);
				}
			}
		}
	}

	int dfs(int v ,int t,long long f){
		if(v == t)return f;
		for(int &i = iter[v];i < G[v].size() ;++i){
			edge &e = G[v][i];
			if(e.cap > 0 && level[v] < level[e.to]){
				long long d = dfs(e.to,t,min(f,e.cap));
				if(d > 0){
					e.cap -= d;
					G[e.to][e.rev].cap += d;
					return d;
				}
			}
		}
		return 0;
	}

	long long solve(int s,int t){
		long long flow = 0;
		while(1){
			bfs(s);
			if(level[t] < 0)return flow;
			fill(iter.begin(), iter.end(), 0);
			long long f;while((f = dfs(s,t,INF)) > 0)flow += f;
		}
	}
};

int getCount(int h, int w, vector<vector<char>> &mp){
	if(h < 0 || w < 0 || h + 1 >= mp.size() || w + 1 >= mp[0].size()) {
		return -1;
	}

	int cou = 0;
	if(mp[h][w] == '#')cou++;
	if(mp[h+1][w] == '#')cou++;
	if(mp[h][w+1] == '#')cou++;
	if(mp[h+1][w+1] == '#')cou++;
	return cou;
}

P searchPairTate(int h, int w, vector<vector<char>> &mp){
	while(1) {
		h++;
		if(mp[h][w] != '#' || mp[h][w+1] != '#'){
			return MP(-1, -1);
		}
		if(getCount(h, w, mp) != 4) {
			break;
		}
	}
	if(getCount(h, w, mp) == 3){
		return MP(h, w);
	}
	else {
		return MP(-1, -1);
	}
}

P searchPairYoko(int h, int w, vector<vector<char>> &mp){
	while(1) {
		w++;
		if(mp[h][w] != '#' || mp[h+1][w] != '#'){
			return MP(-1, -1);
		}
		if(getCount(h, w, mp) != 4) {
			break;
		}
	}
	if(getCount(h, w, mp) == 3){
		return MP(h, w);
	}
	else {
		return MP(-1, -1);
	}
}

bool intersect(pair<P, P> a, pair<P, P> b){
	if(b.FI.SE < a.FI.SE || b.FI.SE > a.SE.SE){
		return false;
	}
	if(a.FI.FI < b.FI.FI || a.FI.FI > b.SE.FI){
		return false;
	}
	return true;
}

void sovle(int h, int w){
	vector<vector<char>> mp(h, vector<char>(w));
	vector<pair<P, P>> tate;
	vector<pair<P, P>> yoko;
	int corner = 0;

	REP(i, h){
		REP(j, w){
			cin >> mp[i][j];
		}
	}

	REP(i, h-1){
		REP(j, w-1){
			int cou = getCount(i, j, mp);
			if(cou == 3){
				corner++;
				P aite = searchPairTate(i, j, mp);
				if(aite.FI != -1){
					tate.EB(MP(i, j), MP(aite.FI, aite.SE));
				}
				aite = searchPairYoko(i, j, mp);
				if(aite.FI != -1){
					yoko.EB(MP(i, j), MP(aite.FI, aite.SE));
				}
			}
		}
	}

	vector<vector<int>> v(tate.size() + yoko.size() + 2);
	Dinic dinic(tate.size() + yoko.size() + 2);
	int start = tate.size() + yoko.size();
	int goal = tate.size() + yoko.size() + 1;

	REP(i, tate.size()){
		dinic.add_edge(start, i, 1);
		REP(j, yoko.size()) {
			if(intersect(yoko[j], tate[i])) {
				dinic.add_edge(i, j + tate.size(), 1);
			}
		}
	}
	REP(i, yoko.size()){
		dinic.add_edge(i + tate.size(), goal, 1);
	}

	cout << corner - (tate.size() + yoko.size() - dinic.solve(start, goal)) + 1 << endl;
}

int main(){
	cin.tie(0);cout.tie(0);ios::sync_with_stdio(false);

	int h, w;
	while(cin >> h >> w,h + w){
		sovle(h, w);
	}

	return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 4060
final_rank: 6

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define REP(i,n) for(ll (i) = (0);(i) < (n);++i)
#define REV(i,n) for(ll (i) = (n) - 1;(i) >= 0;--i)
#define PB push_back
#define EB emplace_back
#define MP make_pair
#define FI first
#define SE second
#define SHOW1d(v,n) {REP(WW,n)cerr << v[WW] << ' ';cerr << endl << endl;}
#define SHOW2d(v,WW,HH) {REP(W_,WW){REP(H_,HH)cerr << v[W_][H_] << ' ';cerr << endl;}cerr << endl;}
#define ALL(v) v.begin(),v.end()
#define Decimal fixed<<setprecision(20)
#define INF 1000000000
#define LLINF 1000000000000000000LL
#define MOD 998244353

typedef long long ll;
typedef pair<ll,ll> P;

struct Dinic {
	struct edge{int to;long long cap,rev;};
	vector<vector<edge>> G;
	vector<int> level;
	vector<int> iter;

	Dinic(int n):G(n, vector<edge>()),level(n),iter(n){}

	void add_edge(int from,int to,long long cap){
		G[from].push_back((edge){to,cap,(ll)G[to].size()});
		G[to].push_back((edge){from,0,(ll)G[from].size()-1});
	}

	void bfs(int s){
		fill(level.begin(), level.end(), -1);
		queue<int> que;
		level[s] = 0;
		que.push(s);
		while(!que.empty())	{
			int v = que.front();que.pop();
			for(int i = 0;i < G[v].size();++i){
				edge &e = G[v][i];
				if(e.cap > 0 && level[e.to] < 0){
					level[e.to] = level[v] + 1;
					que.push(e.to);
				}
			}
		}
	}

	int dfs(int v ,int t,long long f){
		if(v == t)return f;
		for(int &i = iter[v];i < G[v].size() ;++i){
			edge &e = G[v][i];
			if(e.cap > 0 && level[v] < level[e.to]){
				long long d = dfs(e.to,t,min(f,e.cap));
				if(d > 0){
					e.cap -= d;
					G[e.to][e.rev].cap += d;
					return d;
				}
			}
		}
		return 0;
	}

	long long solve(int s,int t){
		long long flow = 0;
		while(1){
			bfs(s);
			if(level[t] < 0)return flow;
			fill(iter.begin(), iter.end(), 0);
			long long f;while((f = dfs(s,t,INF)) > 0)flow += f;
		}
	}
};

int getCount(int h, int w, vector<vector<char>> &mp){
	if(h < 0 || w < 0 || h + 1 >= mp.size() || w + 1 >= mp[0].size()) {
		return -1;
	}

	int cou = 0;
	if(mp[h][w] == '#')cou++;
	if(mp[h+1][w] == '#')cou++;
	if(mp[h][w+1] == '#')cou++;
	if(mp[h+1][w+1] == '#')cou++;
	return cou;
}

P searchPairTate(int h, int w, vector<vector<char>> &mp){
	while(1) {
		h++;
		if(mp[h][w] != '#' || mp[h][w+1] != '#'){
			return MP(-1, -1);
		}
		if(getCount(h, w, mp) != 4) {
			break;
		}
	}
	if(getCount(h, w, mp) == 3){
		return MP(h, w);
	}
	else {
		return MP(-1, -1);
	}
}

P searchPairYoko(int h, int w, vector<vector<char>> &mp){
	while(1) {
		w++;
		if(mp[h][w] != '#' || mp[h+1][w] != '#'){
			return MP(-1, -1);
		}
		if(getCount(h, w, mp) != 4) {
			break;
		}
	}
	if(getCount(h, w, mp) == 3){
		return MP(h, w);
	}
	else {
		return MP(-1, -1);
	}
}

bool intersect(pair<P, P> a, pair<P, P> b){
	if(b.FI.SE < a.FI.SE || b.FI.SE > a.SE.SE){
		return false;
	}
	if(a.FI.FI < b.FI.FI || a.FI.FI > b.SE.FI){
		return false;
	}
	return true;
}

void sovle(int h, int w){
	vector<vector<char>> mp(h, vector<char>(w));
	vector<pair<P, P>> tate;
	vector<pair<P, P>> yoko;
	int corner = 0;

	REP(i, h){
		REP(j, w){
			cin >> mp[i][j];
		}
	}

	REP(i, h-1){
		REP(j, w-1){
			int cou = getCount(i, j, mp);
			if(cou == 3){
				corner++;
				P aite = searchPairTate(i, j, mp);
				if(aite.FI != -1){
					tate.EB(MP(i, j), MP(aite.FI, aite.SE));
				}
				aite = searchPairYoko(i, j, mp);
				if(aite.FI != -1){
					yoko.EB(MP(i, j), MP(aite.FI, aite.SE));
				}
			}
		}
	}

	vector<vector<int>> v(tate.size() + yoko.size() + 2);
	Dinic dinic(tate.size() + yoko.size() + 2);
	int start = tate.size() + yoko.size();
	int goal = tate.size() + yoko.size() + 1;

	REP(i, tate.size()){
		//cout << tate[i].FI.FI << " " << tate[i].FI.SE << " " << tate[i].SE.FI << " " << tate[i].SE.SE << endl;
		dinic.add_edge(start, i, 1);
		REP(j, yoko.size()) {
			if(intersect(yoko[j], tate[i])) {
				//cout << tate[i].FI.FI << " " << tate[i].FI.SE << " " << tate[i].SE.FI << " " << tate[i].SE.SE << " " << yoko[i].FI.FI << " " << yoko[i].FI.SE << " " << yoko[i].SE.FI << " " << yoko[i].SE.SE << endl;
				dinic.add_edge(i, j + tate.size(), 1);
			}
		}
	}
	REP(i, yoko.size()){
		//cout << yoko[i].FI.FI << " " << yoko[i].FI.SE << " " << yoko[i].SE.FI << " " << yoko[i].SE.SE << endl;
		dinic.add_edge(i + tate.size(), goal, 1);
	}

	//cout << "coner tate yoko " << corner << " " << tate.size() << " " << yoko.size() << " " << dinic.solve(start, goal) << endl;
	cout << corner - (tate.size() + yoko.size() - dinic.solve(start, goal)) + 1 << endl;
}

int main(){
	cin.tie(0);cout.tie(0);ios::sync_with_stdio(false);

	int h, w;
	while(cin >> h >> w,h + w){
		sovle(h, w);
	}

	return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 4104
final_rank: 7

code_snippet: #include <bits/stdc++.h>
using namespace std;

class Increase {
public:
  int n, flowMax = 1000000;
  vector<map<int, int>> p;
  vector<int> parent;
  vector<int> stack;

  Increase(int n, vector<map<int, int>> &p) : n(n), p(p) { parent.resize(n); }

  int dfs(int id, int from, int flow) {
    if (parent[id] != id)
      return 0;
    stack.push_back(id);
    parent[id] = from;
    if (id == n - 1)
      return flow;
    for (auto &v : p[id])
      if (v.second > 0) {
        int next = dfs(v.first, id, min(flow, v.second));
        if (next > 0)
          return next;
      }
    return 0;
  }

  int solve() {
    int ret = 0, flow;
    for (int i = 0; i < n; i++)
      parent[i] = i;

    while ((flow = dfs(0, -1, flowMax)) > 0) {
      int now = n - 1;
      ret += flow;
      while (parent[now] >= 0) {
        p[parent[now]][now] -= flow;
        p[now][parent[now]] += flow;
        now = parent[now];
      }
      for (auto &v : stack)
        parent[v] = v;
      stack.clear();
    }
    return ret;
  }
};

int main() {
  while (true) {
    int h, w;
    cin >> h >> w;
    if (h == 0)
      break;
    vector<string> mp(h + 2);
    for (int i = 0; i < w + 2; i++) {
      mp[0].push_back('.');
      mp[h + 1].push_back('.');
    }

    for (int i = 0; i < h; i++) {
      string s;
      cin >> s;
      mp[i + 1] = "." + s + ".";
    }

    h += 2;
    w += 2;

    int n = 0;
    vector<vector<int>> used(h, vector<int>(w));
    vector<pair<int, int>> edge;

    // 横
    int yoko = 1;
    for (int i = 0; i < h - 1; i++) {
      for (int j = 0; j < w - 1; j++) {
        // 右
        if (mp[i][j] == '.' && mp[i][j + 1] == '#' && mp[i + 1][j] == '#' &&
            mp[i + 1][j + 1] == '#') {
          n++;
          int ii = i, jj = j + 1;
          while (mp[ii][jj] == '#' && mp[ii + 1][jj] == '#') {
            jj++;
          }
          if (mp[ii][jj] == '#' || mp[ii + 1][jj] == '#') {
            int now = yoko++;
            for (jj -= 1; jj > j; jj--) {
              used[ii][jj] = now;
            }
          }
        } else if (mp[i][j] == '#' && mp[i][j + 1] == '.' &&
                   mp[i + 1][j] == '#' && mp[i + 1][j + 1] == '#') {
          n++;
        } else if (mp[i][j] == '#' && mp[i][j + 1] == '#' &&
                   mp[i + 1][j] == '.' && mp[i + 1][j + 1] == '#') {
          n++;
          int ii = i, jj = j + 1;
          while (mp[ii][jj] == '#' && mp[ii + 1][jj] == '#') {
            jj++;
          }
          if (mp[ii][jj] == '#' || mp[ii + 1][jj] == '#') {
            int now = yoko++;
            for (jj -= 1; jj > j; jj--) {
              used[ii][jj] = now;
            }
          }
        } else if (mp[i][j] == '#' && mp[i][j + 1] == '#' &&
                   mp[i + 1][j] == '#' && mp[i + 1][j + 1] == '.') {
          n++;
        } else if (mp[i][j] == '#' && mp[i][j + 1] == '.' &&
                   mp[i + 1][j] == '.' && mp[i + 1][j + 1] == '.') {
          n++;
        } else if (mp[i][j] == '.' && mp[i][j + 1] == '#' &&
                   mp[i + 1][j] == '.' && mp[i + 1][j + 1] == '.') {
          n++;
        } else if (mp[i][j] == '.' && mp[i][j + 1] == '.' &&
                   mp[i + 1][j] == '#' && mp[i + 1][j + 1] == '.') {
          n++;
        } else if (mp[i][j] == '.' && mp[i][j + 1] == '.' &&
                   mp[i + 1][j] == '.' && mp[i + 1][j + 1] == '#') {
          n++;
        }
      }
    }

    int tate = yoko;
    for (int i = 0; i < h - 1; i++) {
      for (int j = 0; j < w - 1; j++) {
        if (mp[i][j] == '.' && mp[i][j + 1] == '#' && mp[i + 1][j] == '#' &&
            mp[i + 1][j + 1] == '#') {
          int ii = i + 1, jj = j;
          while (mp[ii][jj] == '#' && mp[ii][jj + 1] == '#') {
            ii++;
          }
          if (mp[ii][jj] == '#' || mp[ii][jj + 1] == '#') {
            int now = tate++;
            for (ii -= 1; ii > i; ii--) {
              if (used[ii][jj] > 0)
                edge.emplace_back(used[ii][jj], now);
              if (used[ii][jj + 1] > 0 && used[ii][jj] == 0)
                edge.emplace_back(used[ii][jj + 1], now);
            }
            if (used[ii][jj + 1] > 0)
              edge.emplace_back(used[ii][jj + 1], now);
          }
        } else if (mp[i][j] == '#' && mp[i][j + 1] == '.' &&
                   mp[i + 1][j] == '#' && mp[i + 1][j + 1] == '#') {
          int ii = i + 1, jj = j;
          while (mp[ii][jj] == '#' && mp[ii][jj + 1] == '#') {
            ii++;
          }
          if (mp[ii][jj] == '#' || mp[ii][jj + 1] == '#') {
            int now = tate++;
            for (ii -= 1; ii > i; ii--) {
              if (used[ii][jj] > 0)
                edge.emplace_back(used[ii][jj], now);
              if (used[ii][jj + 1] > 0 && used[ii][jj] == 0)
                edge.emplace_back(used[ii][jj + 1], now);
            }
            if (used[ii][jj] > 0)
              edge.emplace_back(used[ii][jj], now);
          }
        }
      }
    }

    int m = tate + 1;
    // source: 0, sink: m - 1, yoko: [1, yoko), tate: [yoko, tate)
    vector<map<int, int>> p(m);
    for (int i = 0; i < yoko - 1; i++) {
      p[0][i + 1] = 1;
    }
    for (int i = yoko; i < tate; i++) {
      p[i][m - 1] = 1;
    }
    for (auto &v : edge) {
      p[v.first][v.second] = 1;
    }

    Increase g(m, p);
    int flow = g.solve();
    int indep = m - 2 - flow;

    cout << n / 2 - 1 - indep << endl;
  }
  return 0;
}
accuracy: 1
time_ms: 110
memory_kb: 4808
final_rank: 12

code_snippet: #include<bits/stdc++.h>
#define X first
#define Y second
#define pb emplace_back
#define FOR(i,a,b) for(int (i)=(a);i<(b);++(i))
#define EFOR(i,a,b) for(int (i)=(a);i<=(b);++(i))
#define rep(X,Y) for (int (X) = 0;(X) < (Y);++(X))
#define REP rep
#define rrep(X,Y) for (int (X) = (Y)-1;(X) >=0;--(X))
#define all(X) (X).begin(),(X).end()
#define eb emplace_back

using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef ll LL;
typedef pii PII;
typedef pll PLL;
const ll MOD=1e9+7;

#define rall(X) (X).rbegin(),(X).rend()
#define UNIQUE(X) (X).erase(unique(all(X)),(X).end())
#define reps(X,S,Y) for (int (X) = S;(X) < (Y);++(X))
#define rreps(X,S,Y) for (int (X) = (Y)-1;(X) >= (S);--(X))
template<class T> inline bool MX(T &l,const T &r){return l<r?l=r,1:0;}
template<class T> inline bool MN(T &l,const T &r){return l>r?l=r,1:0;}

const long long INF = (1ll << 50);
struct graph {
  typedef long long flow_type;
  struct edge {
    int src, dst;
    flow_type capacity, flow;
    size_t rev;
  };
  int n;
  vector<vector<edge>> adj;
  graph(int n) : n(n), adj(n) { }
  void add_edge(int src, int dst, flow_type capacity) {
    adj[src].push_back({src, dst, capacity, 0, adj[dst].size()});
    adj[dst].push_back({dst, src, 0, 0, adj[src].size()-1});
  }
  flow_type max_flow(int s, int t) {
    vector<int> level(n), iter(n);
    function<int(void)> levelize = [&]() { // foward levelize
      level.assign(n, -1); level[s] = 0;
      queue<int> Q; Q.push(s);
      while (!Q.empty()) {
        int u = Q.front(); Q.pop();
        if (u == t) break;
        for (auto &e: adj[u]) {
          if (e.capacity > e.flow && level[e.dst] < 0) {
            Q.push(e.dst);
            level[e.dst] = level[u] + 1;
          }
        }
      }
      return level[t];
    };
    function<flow_type(int, flow_type)> augment = [&](int u, flow_type cur) {
      if (u == t) return cur;
      for (int &i = iter[u]; i < adj[u].size(); ++i) {
        edge &e = adj[u][i], &r = adj[e.dst][e.rev];
        if (e.capacity > e.flow && level[u] < level[e.dst]) {
          flow_type f = augment(e.dst, min(cur, e.capacity - e.flow));
          if (f > 0) {
            e.flow += f;
            r.flow -= f;
            return f;
          }
        }
      }
      return flow_type(0);
    };
    for (int u = 0; u < n; ++u) // initialize
      for (auto &e: adj[u]) e.flow = 0;

    flow_type flow = 0;
    while (levelize() >= 0) {
      fill(all(iter), 0);
      for (flow_type f; (f = augment(s, INF)) > 0; )
        flow += f;
    }
    return flow;
  }
};


int H;
int W;
char field[114][114];

void Color(vector<vector<int>> &es, vector<int> &col, int v, int c=1) {
  col[v] = c;
  for (int u : es[v]) {
    if (col[u]) {
      assert(col[u] != col[v]);
      continue;
    }

    Color(es, col, u, 3-c);
  }
}

int Solve() {
  rep(i, H) {
    rep(j, W) {
      cin >> field[i][j];
    }
  }

  int ans = 1;
  map<pii, pii> hole;
  reps(i, 1, H) {
    reps(j, 1, W) {
      int cnt = 0;
      vector<pii> holes;
      rep(k, 2) {
        rep(l, 2) {
          if (field[i-k][j-l] != '#') holes.eb(pii(k, l));
        }
      }

      if (holes.size() == 1) {
        ans++;
        hole[pii(i, j)] = holes[0];
      }
    }
  }

  if (hole.empty()) return ans;

  using Seg = pair<pii, pii>;
  vector<Seg> segs;
  map<Seg, int> mp;
  reps(i, 1, H) {
    reps(j, 1, W) {
      if (!hole.count(pii(i, j))) continue;

      auto p = hole[pii(i, j)];
      if (p.X) {
        reps(ni, i+1, H) {
          int cnt = 0;
          rep(k, 2) {
            rep(l, 2) {
              cnt += (field[ni-k][j-l] == '#');
            }
          }
          
          if (cnt <= 2) break;
          if (hole.count(pii(ni, j))) {
            if (hole[pii(ni, j)].X == 0) {
              segs.eb(Seg(pii(i, j), pii(ni, j)));
              int v = mp.size();
              mp[segs.back()] = v;
            }
            break;
          } 
          assert(cnt == 4);
        }
      }

      if (p.Y) {
        reps(nj, j+1, W) {
          int cnt = 0;
          rep(k, 2) {
            rep(l, 2) {
              cnt += (field[i-k][nj-l] == '#');
            }
          }
          
          if (cnt <= 2) break;
          if (hole.count(pii(i, nj))) {
            if (hole[pii(i, nj)].Y == 0) {
              segs.eb(Seg(pii(i, j), pii(i, nj)));
              int v = mp.size();
              mp[segs.back()] = v;
            }
            break;
          } 
          assert(cnt == 4);
        }
      }
    }
  }
  
  vector<vector<int>> es(mp.size());
  for (auto &s : segs) {
    bool sd = s.X.X == s.Y.X;
    for (auto &t : segs) {
      bool td = t.X.X == t.Y.X;
      if (sd == td) continue;
      if (!sd) swap(s, t);
      if (min(s.X.Y, s.Y.Y) <= t.X.Y && t.X.Y <= max(s.X.Y, s.Y.Y)) {
        if (min(t.X.X, t.Y.X) <= s.X.X && s.X.X <= max(t.X.X, t.Y.X)) {
          int u = mp[s];
          int v = mp[t];
          es[u].eb(v);
          es[v].eb(u);
        }
      }
      if (!sd) swap(s, t);
    }
  }

  vector<int> col(mp.size());
  rep(v, mp.size()) {
    if (col[v]) continue;
    Color(es, col, v);
  }

  //vector<pii> rev(mp.size());
  //for (auto &p : mp) {
  //  rev[p.Y] = p.X;
  //}

  //rep(v, mp.size()) {
  //  for (int u : es[v]) {
  //    if (v > u) continue;
  //    auto a = rev[v];
  //    cout << "(" << a.X << ", " << a.Y << "), ";
  //    auto b = rev[u];
  //    cout << "(" << b.X << ", " << b.Y << ")" << endl;
  //  }
  //}

  int S = mp.size();
  int T = mp.size()+1;
  graph G(mp.size()+2);
  rep(v, mp.size()) {
    if (col[v] == 1) {
      G.add_edge(S, v, 1);
    } else {
      G.add_edge(v, T, 1);
    }
  }

  rep(v, mp.size()) {
    if (col[v] == 2) continue;
    for (int u : es[v]) {
      assert(col[u] == 2);
      G.add_edge(v, u, 1);
    }
  }
  
  int m = mp.size();
  return ans - (m - G.max_flow(S, T));
}

signed main(){
  ios_base::sync_with_stdio(false);
  cout<<fixed<<setprecision(0);

  while (1) {
    cin >> H >> W;
    if (H == 0) return 0;
    cout << Solve() << "\n";
  }
}
accuracy: 1
time_ms: 280
memory_kb: 5588
final_rank: 16

code_snippet: #include "bits/stdc++.h"
#pragma GCC optimize("Ofast")

#pragma GCC target("avx,avx2")
#include<unordered_map>
#include<unordered_set>
#pragma warning(disable:4996)
using namespace std;
using ll=long long ;
#define Whats(x) //cout<<#x<<" is "<<(x)<<endl


////フォード - ファルカーソン法 O(Flow|E|)

typedef int Weight;

const Weight INF = 1e9;
const Weight ZERO = 0;
struct Edge {
	int src, dst;
	Weight weight;
	int rev;
	int id;
	Edge(int src_, int dst_, Weight weight_, const int rev_, const int id_) :
		src(src_), dst(dst_), weight(weight_), rev(rev_), id(id_) { }
	Edge(int src_, int dst_, Weight weight_, const int rev_) :
		src(src_), dst(dst_), weight(weight_), rev(rev_) { }
	Edge() :src(0), dst(0), weight(0) {
	}
};
bool operator < (const Edge &e, const Edge &f) {
	return e.weight != f.weight ? e.weight > f.weight : // !!INVERSE!!
	e.src != f.src ? e.src < f.src : e.dst < f.dst;
}
typedef vector<Edge> Edges;
typedef vector<Edges> Graph;

typedef vector<Weight> Array;
typedef vector<Array> Matrix;
void add_edge(Graph&g, int from, int to, int cap)
{
	g[from].push_back(Edge(from, to, cap, (int)g[to].size()));
	g[to].push_back(Edge(to, from, 0, (int)g[from].size() - 1));
}
//二つを繋ぐ線を完全に消す
void erase_edge(Graph&g, const  int from, const int to) {
	g[from].erase(remove_if(g[from].begin(), g[from].end(), [=](const Edge&e) {return e.dst == to; }), g[from].end());
	g[to].erase(remove_if(g[to].begin(), g[to].end(), [=](const Edge&e) {return e.dst == from; }), g[to].end());
}
int dfs(vector<int>&used, Graph&g, int now, int t, int f)
{
	if (now == t) {
		return f;
	}
	used[now] = true;
	for (int i = 0; i<g[now].size(); ++i) {
		Edge &e = g[now][i];
		if (!used[e.dst] && e.weight>0) {
			int d = dfs(used, g, e.dst, t, min(f, e.weight));
			if (d > 0) {
				e.weight -= d;
				g[e.dst][e.rev].weight += d;
				return d;
			}
		}
	}
	return 0;
}
Weight fold_falc(Graph g, const int start, const int goal) {
	Weight ans = 0;
	while (1) {
		vector<int>used(g.size());
		int flow = dfs(used, g, start, goal, INF);
		if (!flow)return ans;
		else ans += flow;
	}
}
struct line {
	int l;
	int u;
	int r;
	int d;
};
int main() {
	while (true) {
		int H,W;cin>>H>>W;
		if(!H)break;
		vector<string>field(H);
		for(int i=0;i<H;++i)cin>>field[i];

		vector<vector<int>>nums(H+1,vector<int>(W+1));
		for (int y = 0; y < H - 1; ++y) {
			for (int x = 0; x < W - 1; ++x) {
				for (int dx = 0; dx < 2; ++dx) {
					for (int dy = 0; dy < 2; ++dy) {
						nums[y+1][x+1]+=field[y+dy][x+dx]=='#';
					}
				}
			}
		}
		vector<pair<int,int>>points;
		for (int j = 1; j <= W; ++j) {
			for (int i = 1; i <= H; ++i) {

				if(nums[i][j]==3)points.push_back(make_pair(j,i));
			}
		}
		vector<line>x_lines,y_lines;
		for (int i = 0; i < points.size(); ++i) {
			for (int j = i+1; j < points.size(); ++j) {
				int l=points[i].first,r=points[j].first;
				int u=points[i].second,d=points[j].second;
				if(l==r){
					int x=l;
					bool ok=true;
					for (int y = u; y < d; ++y) {
						if(field[y][x-1]=='.')ok=false;
						if(field[y][x]=='.')ok=false;
					}
					if (ok) {
						y_lines.push_back(line{ l,u,r,d });
					}
				}
				if(u==d){
					int y=u;
					
					bool ok = true;
					for (int x = l; x < r; ++x) {
						if(field[y][x]=='.')ok=false;
						if(field[y-1][x]=='.')ok=false;
					}
					if (ok) {
						x_lines.push_back(line{ l,u,r,d });
					}
				}
			}
		}
		const int start=0;
		const int xl=start+1;
		const int yl=xl+x_lines.size();
		const int goal=yl+y_lines.size();
		Graph g(goal+1);
		for (int i = 0; i < x_lines.size(); ++i) {
			for (int j = 0; j < y_lines.size(); ++j) {
				auto axl=x_lines[i];
				auto ayl=y_lines[j];
				bool ok=true;
				//Whats(axl.l);Whats(axl.r);
			
				if (axl.l <= ayl.l&&ayl.l <= axl.r) {
					if (ayl.u <= axl.u&&axl.u <= ayl.d) {
						ok=false;
						//cout<<"ng"<<endl;
					}
				}
				if (!ok) {
					add_edge(g,xl+i,yl+j,10000);
				}
			}
		}
		for (int i = 0; i < x_lines.size(); ++i) {
			add_edge(g,start,xl+i,1);
			add_edge(g,xl+i,goal,0);
		}
		for (int i = 0; i < y_lines.size(); ++i) {
			add_edge(g,start,yl+i,0);
			add_edge(g,yl+i,goal,1);
		}
		int flow=fold_falc(g,start,goal);
		Whats(points.size());
		Whats(x_lines.size());
		Whats(y_lines.size());
		Whats(flow);
		int answer=1+points.size()-x_lines.size()-y_lines.size()+flow;
		cout<<answer<<endl;
	}
	return 0;
}
accuracy: 1
time_ms: 650
memory_kb: 4732
final_rank: 19

code_snippet: #define _CRT_SECURE_NO_WARNINGS

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <limits>
#include <ctime>
#include <cassert>
#include <map>
#include <set>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
#include <sstream>
#include <stack>
#include <queue>
#include <numeric>
#include <iterator>
#include <bitset>
#include <unordered_map>
#include <unordered_set>

using namespace std;
#define FOR(i,n) for(int i = 0; i < (n); i++)
#define sz(c) ((int)c.size())
#define ten(n) ((int)1e##n)
using ll = long long;
using Pii = pair<int, int>;
using Pll = pair<ll, ll>;

template<typename ...> static inline int getchar_unlocked(void) { return getchar(); }
template<typename ...> static inline void putchar_unlocked(int c) { putchar(c); }
#define mygc(c) (c)=getchar_unlocked()
#define mypc(c) putchar_unlocked(c)
void reader(int& x) { int k, m = 0; x = 0; for (;;) { mygc(k); if (k == '-') { m = 1; break; }if ('0' <= k&&k <= '9') { x = k - '0'; break; } }for (;;) { mygc(k); if (k<'0' || k>'9')break; x = x * 10 + k - '0'; }if (m) x = -x; }
void reader(ll& x) { int k, m = 0; x = 0; for (;;) { mygc(k); if (k == '-') { m = 1; break; }if ('0' <= k&&k <= '9') { x = k - '0'; break; } }for (;;) { mygc(k); if (k<'0' || k>'9')break; x = x * 10 + k - '0'; }if (m) x = -x; }
int reader(char c[]) { int i, s = 0; for (;;) { mygc(i); if (i != ' '&&i != '\n'&&i != '\r'&&i != '\t'&&i != EOF) break; }c[s++] = i; for (;;) { mygc(i); if (i == ' ' || i == '\n' || i == '\r' || i == '\t' || i == EOF) break; c[s++] = i; }c[s] = '\0'; return s; }
int reader(string& c) { int i; for (;;) { mygc(i); if (i != ' '&&i != '\n'&&i != '\r'&&i != '\t'&&i != EOF) break; }c.push_back(i); for (;;) { mygc(i); if (i == ' ' || i == '\n' || i == '\r' || i == '\t' || i == EOF) break; c.push_back(i); }; return sz(c); }
template <class T, class S> void reader(T& x, S& y) { reader(x); reader(y); }
template <class T, class S, class U> void reader(T& x, S& y, U& z) { reader(x); reader(y); reader(z); }
template <class T, class S, class U, class V> void reader(T& x, S& y, U& z, V & w) { reader(x); reader(y); reader(z); reader(w); }
void writer(int x, char c) { int s = 0, m = 0; char f[10]; if (x<0)m = 1, x = -x; while (x)f[s++] = x % 10, x /= 10; if (!s)f[s++] = 0; if (m)mypc('-'); while (s--)mypc(f[s] + '0'); mypc(c); }
void writer(ll x, char c) { int s = 0, m = 0; char f[20]; if (x<0)m = 1, x = -x; while (x)f[s++] = x % 10, x /= 10; if (!s)f[s++] = 0; if (m)mypc('-'); while (s--)mypc(f[s] + '0'); mypc(c); }
void writer(const char c[]) { int i; for (i = 0; c[i] != '\0'; i++)mypc(c[i]); }
void writer(const string& x, char c) { int i; for (i = 0; x[i] != '\0'; i++)mypc(x[i]); mypc(c); }
void writer(const char x[], char c) { int i; for (i = 0; x[i] != '\0'; i++)mypc(x[i]); mypc(c); }
template<class T> void writerLn(T x) { writer(x, '\n'); }
template<class T, class S> void writerLn(T x, S y) { writer(x, ' '); writer(y, '\n'); }
template<class T, class S, class U> void writerLn(T x, S y, U z) { writer(x, ' '); writer(y, ' '); writer(z, '\n'); }
template<class T, class S, class U, class V> void writerLn(T x, S y, U z, V v) { writer(x, ' '); writer(y, ' '); writer(z, ' '); writer(v, '\n'); }
template<class T> void writerArr(T x[], int n) { if (!n) { mypc('\n'); return; }FOR(i, n - 1)writer(x[i], ' '); writer(x[n - 1], '\n'); }
template<class T> void writerArr(vector<T>& x) { writerArr(x.data(), (int)x.size()); }

template<class T> void chmin(T& a, const T& b) { if (a > b) a = b; }
template<class T> void chmax(T& a, const T& b) { if (a < b) a = b; }

template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }
ll mod_pow(ll a, ll n, ll mod) {
	ll ret = 1;
	ll p = a % mod;
	while (n) {
		if (n & 1) ret = ret * p % mod;
		p = p * p % mod;
		n >>= 1;
	}
	return ret;
}
template<class T> T extgcd(T a, T b, T& x, T& y) { for (T u = y = 1, v = x = 0; a;) { T q = b / a; swap(x -= q * u, u); swap(y -= q * v, v); swap(b -= q * a, a); } return b; }
ll mod_inv(ll a, ll m) { ll x, y; extgcd<ll>(a, m, x, y); return (m + x % m) % m; }

#ifdef _MSC_VER
template <typename ... Args>
void debugPrintf(const char *format, Args const & ... args) {
	fprintf(stderr, format, args ...);
	fflush(stderr);
}
#else
#define debugPrintf(...)
#endif

const int MAX_N = 100000;
int match[MAX_N];
bool used[MAX_N];

bool dfs(vector<vector<int>>& G, int v) {
	used[v] = true;
	for (auto u : G[v]) {
		int w = match[u];
		if (w < 0 || !used[w] && dfs(G, w)) {
			match[v] = u;
			match[u] = v;
			return true;
		}
	}
	return false;
}

int bipartite_matching(vector<vector<int>>& G) {
	int ret = 0;
	memset(match, -1, sizeof(match));
	FOR(v, sz(G)) {
		if (match[v] < 0) {
			memset(used, 0, sizeof(used));
			if (dfs(G, v)) ret++;
		}
	}
	return ret;
}

int main() {
	int n, m;
	while (cin >> n >> m && n) {
		static char b[100][101];
		FOR(i, n) cin >> b[i];
		vector<vector<int>> f(n - 1);
		FOR(i, n - 1) FOR(j, m - 1) {
			int cur = 0;
			FOR(x, 2) FOR(y, 2) {
				int tmp = b[i + x][j + y] == '#';
				cur += tmp *(1 << (2 * x + y));
			}
			f[i].push_back(cur);
		}

		int specialCount = 0;
		FOR(i, n - 1) FOR(j, m - 1) {
			int t = f[i][j];
			bool ok = false;
			FOR(k, 4) if (t == 15 - (1 << k)) ok = true;
			if (!ok) continue;
			specialCount++;
		}

		vector<pair<Pii, Pii>> g[2];
		auto add_edge = [&](Pii a, Pii b) {
			if (a.first == b.first) g[0].emplace_back(a, b);
			else g[1].emplace_back(a, b);
		};

		FOR(i, n - 1) {
			Pii tmp = { -1,-1 };
			FOR(j, m - 1) {
				int cur = f[i][j];
				if (cur == 15 - 1 || cur == 15 - 4) {
					tmp = Pii(i, j);
				} else if(cur == 15 - 2 || cur == 15 - 8) {
					if(tmp.first != -1) {
						add_edge(tmp, Pii(i, j));
						tmp.first = -1;
					}
				} else if (cur == 15) {
					continue;
				} else {
					tmp.first = -1;
				}
			}
		}
		FOR(j, m - 1) {
			Pii tmp = { -1,-1 };
			FOR(i, n - 1) {
				int cur = f[i][j];
				if (cur == 15 - 1 || cur == 15 - 2) {
					tmp = Pii(i, j);
				} else if (cur == 15 - 4 || cur == 15 - 8) {
					if (tmp.first != -1) {
						add_edge(tmp, Pii(i, j));
						tmp.first = -1;
					}
				} else if (cur == 15) {
					continue;
				} else {
					tmp.first = -1;
				}
			}
		}

		vector<vector<int>> g2(sz(g[0]) + sz(g[1]));
		FOR(i, sz(g[0])) {
			FOR(j, sz(g[1])) {
				auto a = g[0][i].first, b = g[0][i].second;
				auto c = g[1][j].first, d = g[1][j].second;

				bool intersected1 = a.second <= c.second && c.second <= b.second;
				bool intersected2 = c.first <= a.first && a.first <= d.first;
				if (intersected1 && intersected2) {
					int u = i, v = sz(g[0]) + j;
					g2[u].push_back(v);
					g2[v].push_back(u);
				}
			}
		}

		int matched = bipartite_matching(g2);
		int unmatched = sz(g2) - matched;
		int ans = specialCount - unmatched + 1;
		cout << ans << endl;
	}
	return 0;
}
accuracy: 1
time_ms: 100
memory_kb: 3872
final_rank: 11

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 99999999999999999
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


#define NUM 8000
#define SIZE 105

enum Type{
	Right_Under,
	Left_Under,
	Right_Up,
	Left_Up,
};

struct Edge{
	Edge(int arg_x1,int arg_y1, int arg_x2,int arg_y2){
		x1 = arg_x1;
		y1 = arg_y1;
		x2 = arg_x2;
		y2 = arg_y2;
	}
	int x1,y1,x2,y2;
};

int H,W;
int V;
vector<int> G[NUM];
int match[NUM];
bool used[NUM];
bool check[SIZE][SIZE][4];
char base_map[SIZE][SIZE];
vector<Edge> TATE,YOKO;


void add_edge(int from,int to){
	G[from].push_back(to);
	G[to].push_back(from);
}

int dfs(int node_id){
	used[node_id] = true;

	for(int i = 0; i < G[node_id].size(); i++){
		int adj_node_id = G[node_id][i],pair_id = match[adj_node_id];
		if((pair_id < 0)||
				(used[pair_id] == false && dfs(pair_id) == true)){
			match[node_id] = adj_node_id;
			match[adj_node_id] = node_id;
			return true;
		}
	}
	return false;
}


int bipartie_matching(){
	int ret = 0;
	for(int i = 0; i < V; i++)match[i] = -1;
	for(int node_id = 0; node_id < V; node_id++){
		if(match[node_id] < 0){
			for(int i = 0; i < V; i++)used[i] = false;
			if(dfs(node_id)){
				ret++;
			}
		}
	}
	return ret;
}

bool rangeCheck(int row,int col){
	if(row >= 0 && row <= H-1 && col >= 0 && col <= W-1){
		return true;
	}else{
		return false;
	}
}

void func(){

	TATE.clear();
	YOKO.clear();

	for(int x = 0; x <= W; x++){
		for(int y = 0; y <= H; y++){
			for(int a = 0; a < 4; a++){
				check[x][y][a] = false;
			}
		}
	}

	for(int row = 0; row < H; row++){
		scanf("%s",base_map[row]);
	}

	int num_cut_point = 0;
	int tmp_x,tmp_y;

	for(int row = 0; row < H; row++){
		for(int col = 0; col < W; col++){
			if(base_map[row][col] == '#')continue;

			//Right_Under
			if(rangeCheck(row+1,col+1) == true && base_map[row][col+1] == '#' &&
					base_map[row+1][col] == '#' && base_map[row+1][col+1] == '#'){

				tmp_x = col+1;
				tmp_y = row+1;

				check[tmp_x][tmp_y][Right_Under] = true;

				num_cut_point++;
			}

			//Left_Under
			if(rangeCheck(row+1,col-1) == true && base_map[row][col-1] == '#' &&
					base_map[row+1][col] == '#' && base_map[row+1][col-1] == '#'){

				tmp_x = col;
				tmp_y = row+1;

				check[tmp_x][tmp_y][Left_Under] = true;

				num_cut_point++;
			}

			//Right_Up
			if(rangeCheck(row-1,col+1) == true && base_map[row-1][col] == '#' &&
					base_map[row-1][col+1] == '#' && base_map[row][col+1] == '#'){

				tmp_x = col+1;
				tmp_y = row;

				check[tmp_x][tmp_y][Right_Up] = true;

				num_cut_point++;
			}

			//Left_Up
			if(rangeCheck(row-1,col-1) == true && base_map[row-1][col] == '#' &&
					base_map[row-1][col-1] == '#' && base_map[row][col-1] == '#'){

				tmp_x = col;
				tmp_y = row;

				check[tmp_x][tmp_y][Left_Up] = true;

				num_cut_point++;
			}
		}
	}

	for(int y = 0; y <= H; y++){
		for(int x = 0; x < W; x++){

			if(check[x][y][Right_Under] == true || check[x][y][Right_Up] == true){

				for(int another = x+1; another <= W; another++){

					if(rangeCheck(y-1,another-1) == false || rangeCheck(y,another) == false ||
							base_map[y-1][another-1] == '.' || base_map[y][another-1] == '.'){
						break;
					}

					if(check[another][y][Left_Under] == true || check[another][y][Left_Up] == true){
						YOKO.push_back(Edge(x,y,another,y));
						break;
					}
				}
			}
		}
	}

	for(int x = 0; x <= W; x++){
		for(int y = 0; y < H; y++){
			if(check[x][y][Right_Under] == true || check[x][y][Left_Under] == true){

				for(int another = y+1; another <= H; another++){

					if(rangeCheck(another-1,x-1) == false || rangeCheck(another-1,x) == false ||
							base_map[another-1][x-1] == '.' || base_map[another-1][x] == '.'){
						break;
					}

					if(check[x][another][Right_Up] == true || check[x][another][Left_Up] == true){
						TATE.push_back(Edge(x,y,x,another));
						break;
					}
				}
			}
		}
	}

	V = (int)TATE.size()+(int)YOKO.size();

	for(int i = 0; i < NUM; i++){
		G[i].clear();
	}

	for(int i = 0; i < YOKO.size(); i++){
		for(int k = 0; k < TATE.size(); k++){
			if(TATE[k].x1 >= YOKO[i].x1 && TATE[k].x1 <= YOKO[i].x2 && YOKO[i].y1 >= TATE[k].y1 && YOKO[i].y1 <= TATE[k].y2){
				add_edge(i,(int)YOKO.size()+k);
			}
		}
	}

	printf("%d\n",num_cut_point-(V-bipartie_matching())+1);
}

int main(){

	while(true){

		scanf("%d %d",&H,&W);
		if(H == 0 && W == 0)break;

		func();
	}

	return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 3868
final_rank: 2