// Arup Guha
// 5/17/2024 (started a few days ago)
// Solution to 2023 SER D1 Problem F: Matrix Fraud

using namespace std;

#include <bits/stdc++.h>

int r;
int c;
vector< vector<int> > m;
vector< vector<int> > dp;
vector< vector<int> > min_table;
vector< vector<int> > cf;

void updateInput(vector<string>& lines);
int getcost(int myr,int mys,int mye) ;
void printdp();
void printmin();

int main() {

    // Read in initial input.
    cin >> r >> c;
    vector<string> lines;
    for (int i=0; i<r; i++) {
        string tmp;
        cin >> tmp;
        lines.push_back(tmp);
    }

    // Create m, making sure c < r and data is transposed accordingly.
    updateInput(lines);

    for (int i=0; i<r; i++)
        cf.push_back(vector<int>(c+1));
    for (int i=0; i<r; i++)
        cf[i][0] = 0;

    // Make cumulative frequency rows.
    for (int i=0; i<r; i++)
        for (int j=1; j<=c; j++)
            cf[i][j] = cf[i][j-1] + m[i][j-1];

    // This is what we'll build from prev.
    for (int j=0; j<c; j++)
        dp.push_back(vector<int>(c));

    for (int j=0; j<c; j++)
        min_table.push_back(vector<int>(c));

    // Fill previous DP table with costs for all possible intervals on first row.
    // Must have 1 in leftmost spot.
    for (int j=0; j<c; j++) {
        for (int k=j; k<c; k++) {
            dp[j][k] = j == 0 ? getcost(0,j,k) : r*c;
            min_table[j][k] = dp[j][k];
        }
    }

    // Run minimums down columns.
    for (int k=0; k<c; k++)
        for (int j=1; j<=k; j++)
            min_table[j][k] = min(min_table[j][k], min_table[j-1][k]);

    // Run minimum across rows.
    for (int j=0; j<c; j++)
        for (int k=j+1; k<c; k++)
            min_table[j][k] = min(min_table[j][k], min_table[j][k-1]);

    for (int myr=1; myr<r; myr++) {

        // We can calculate DP table fast from min table.
        for (int sI=0; sI<c; sI++) {
            for (int eI=sI; eI<c; eI++) {
                int bestold = min_table[sI][eI];
                if (sI>0) bestold = min(bestold, min_table[sI-1][sI-1]);
                dp[sI][eI] = bestold+getcost(myr, sI, eI);
            }
        }

        // Initialize min table to be dp.
        for (int sI=0; sI<c; sI++)
            for (int eI=sI; eI<c; eI++)
                min_table[sI][eI] = dp[sI][eI];

        // Run mins down cols.
        for (int k=0; k<c; k++)
            for (int j=1; j<=k; j++)
                min_table[j][k] = min(min_table[j][k], min_table[j-1][k]);

        // Run minimum across rows.
        for (int j=0; j<c; j++)
            for (int k=j+1; k<c; k++)
                min_table[j][k] = min(min_table[j][k], min_table[j][k-1]);
    }

    // Take the best answer that ends at the last column.
    int res = dp[0][c-1];
    for (int i=1; i<c; i++)
        res = min(res, dp[i][c-1]);
    cout << res << endl;

    return 0;
}

// Returns the cost of putting all 1s in [mys..mye] on row myr.
int getcost(int myr,int mys,int mye) {
    int onesleft = cf[myr][mys];
    int onesmid = cf[myr][mye+1] - cf[myr][mys];
    int onesright = cf[myr][c] - cf[myr][mye+1];
    return onesleft + onesright + (mye-mys+1) - onesmid;
}

// Updates the input so c < r. (Transposes if necessary.)
void updateInput(vector<string>& lines) {

    // Get the easy case out of the way.
    if (c < r) {
        for (int i=0; i<r; i++) {
            m.push_back(vector<int>());
            for (int j=0; j<c; j++)
                m[i].push_back(lines[i][j]-'0');
        }
        return;
    }

    // Flipping columns and rows.
    for (int i=0; i<c; i++)
        m.push_back(vector<int>(r));

    // Backwards through original columns.
    for (int i=c-1; i>=0; i--)
        for (int j=r-1; j>=0; j--)
            m[c-1-i][r-1-j] = lines[j][i]-'0';

    // Our dimensions are different now.
    int tmp = r;
    r = c;
    c = tmp;
}
