A binary matrix is called good if every even length square sub-matrix has an odd number of ones. Given a binary matrix $$$a$$$ consisting of $$$n$$$ rows and $$$m$$$ columns, determine the minimum number of cells you need to change to make it good, or report that there is no way to make it good at all. All the terms above have their usual meanings — refer to the Notes section for their formal definitions.

The first line of input contains two integers $$$n$$$ and $$$m$$$ ($$$1 \leq n \leq m \leq 10^6$$$ and $$$n\cdot m \leq 10^6$$$)  — the number of rows and columns in $$$a$$$, respectively.  The following $$$n$$$ lines each contain $$$m$$$ characters, each of which is one of 0 and 1. If the $$$j$$$-th character on the $$$i$$$-th line is 1, then $$$a_{i,j} = 1$$$. Similarly, if the $$$j$$$-th character on the $$$i$$$-th line is 0, then $$$a_{i,j} = 0$$$.

Output the minimum number of cells you need to change to make $$$a$$$ good, or output $$$-1$$$ if it's not possible at all.

NoteIn the first case, changing $$$a_{1,1}$$$ to $$$0$$$ and $$$a_{2,2}$$$ to $$$1$$$ is enough. You can verify that there is no way to make the matrix in the second case good. Some definitions —   A binary matrix is one in which every element is either $$$1$$$ or $$$0$$$.  A sub-matrix is described by $$$4$$$ parameters — $$$r_1$$$, $$$r_2$$$, $$$c_1$$$, and $$$c_2$$$; here, $$$1 \leq r_1 \leq r_2 \leq n$$$ and $$$1 \leq c_1 \leq c_2 \leq m$$$.  This sub-matrix contains all elements $$$a_{i,j}$$$ that satisfy both $$$r_1 \leq i \leq r_2$$$ and $$$c_1 \leq j \leq c_2$$$.  A sub-matrix is, further, called an even length square if $$$r_2-r_1 = c_2-c_1$$$ and $$$r_2-r_1+1$$$ is divisible by $$$2$$$.

package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
)

var in *bufio.Reader
var out *bufio.Writer

func printf(x string, a ...interface{}) (n int, err error) {
	return fmt.Fprintf(out, x, a...)
}

func scanf(x string, a ...interface{}) (n int, err error) {
	return fmt.Fscanf(in, x, a...)
}

type priorityQueue [][2]int

func (pq priorityQueue) Len() int {
	return len(pq)
}

func (pq priorityQueue) Less(i, j int) bool {
	return pq[i][1] > pq[j][1]
}

func (pq priorityQueue) Swap(i, j int) {
	pq[i], pq[j] = pq[j], pq[i]
}

func (pq *priorityQueue) Push(x interface{}) {
	*pq = append(*pq, x.([2]int))
}

func (pq *priorityQueue) Pop() (res interface{}) {
	res = (*pq)[len(*pq)-1]
	*pq = (*pq)[:len(*pq)-1]
	return res
}

var err error

func next() (n string) {
	for {
		_, err = scanf("%v", &n)
		if err == nil {
			return n
		}
		if err.Error() == "EOF" {
			return ""
		}
	}
}

func nextInt() (n int) {
	for {
		_, err = scanf("%v", &n)
		if err == nil {
			return n
		}
		if err.Error() == "EOF" {
			return 0
		}
	}
}

func nextLong() (n int64) {
	for {
		_, err = scanf("%v", &n)
		if err == nil {
			return n
		}
		if err.Error() == "EOF" {
			return 0
		}
	}
}

func nextDouble() (n float64) {
	for {
		_, err = scanf("%v", &n)
		if err == nil {
			return n
		}
		if err.Error() == "EOF" {
			return 0
		}
	}
}

func Fill(a []int, v int) {
	for i := range a {
		a[i] = v
	}
}

func FillB(a []bool, v bool) {
	for i := range a {
		a[i] = v
	}
}

func Min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func Max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

var (
	dx [4]int = [4]int{-1, 0, 1, 0}
	dy [4]int = [4]int{0, 1, 0, -1}
)

const (
	mod = 1000000007
)

func mult(a, b int64) int64 {
	return (a * b) % mod
}
func add(a, b int64) int64 {
	return (a + b) % mod
}

func sub(a, b int64) int64 {
	return (a - b + mod) % mod
}

func solve() {
	n, m := nextInt(), nextInt()
	if n >= 4 {
		printf("-1\n")
		return
	}
	arr := make([][]byte, n)
	for i := range arr {
		arr[i] = []byte(next())
		for j := range arr[i] {
			arr[i][j] -= '0'
		}
	}
	dp := make([][]int, 1<<n)
	for i := range dp {
		dp[i] = make([]int, m+1)
	}
	for i := m - 1; i >= 0; i-- {
		for j := 0; j < 1<<n; j++ {
			dp[j][i] = n * m
		outer:
			for k := 0; k < 1<<n; k++ {
				if i != 0 {
					for l := 0; l < n-1; l++ {
						if !((j&(1<<l) != 0) != (j&(1<<(l+1)) != 0) != (k&(1<<l) != 0) != (k&(1<<(l+1)) != 0)) {
							continue outer
						}
					}
				}
				res := dp[k][i+1]
				for l := 0; l < n; l++ {
					if (k&(1<<l) != 0) != (arr[l][i] != 0) {
						res++
					}
				}
				if res < dp[j][i] {
					dp[j][i] = res
				}
			}
		}
	}
	printf("%d\n", dp[0][0])
}

func main() {
	in = bufio.NewReaderSize(os.Stdin, 8192)
	out = bufio.NewWriterSize(os.Stdout, 8192)
	if false {
		name := "in"
		file1, err := os.Open(name + ".in")
		if err != nil {
			log.Fatal(err)
		}
		// file2, err := os.Create(name + ".out")
		// if err != nil {
		// 	log.Fatal(err)
		// }
		in = bufio.NewReaderSize(file1, 8192)
		// out = bufio.NewWriterSize(file2, 8192)
	}
	defer out.Flush()

	// for t := nextInt(); t > 0; t-- {
	solve()
	// }
}

package main

import (
	"bufio"
	"fmt"
	"io"
	"math"
	"math/bits"
	"os"
)

func main() {
	io := NewFastIO(os.Stdin, os.Stdout)
	defer io.Flush()

	n, m := io.NextInt(), io.NextInt()
	g := make([][]int, n)
	for i := range g {
		s := io.Next()
		g[i] = make([]int, m)
		for j, c := range s {
			g[i][j] = int(c - '0')
		}
	}
	if n < m {
		g2 := make([][]int, m)
		for i := 0; i < m; i++ {
			g2[i] = make([]int, n)
			for j := range g2[i] {
				g2[i][j] = g[j][i]
			}
		}
		n, m, g = m, n, g2
	}

	onesCount := func(x int) int {
		return bits.OnesCount(uint(x))
	}
	min := func(i, j int) int {
		if i < j {
			return i
		}
		return j
	}

	var gg []int
	solve2 := func() int {
		dp := make([][]int, n)
		for i := 0; i < n; i++ {
			dp[i] = make([]int, 4)
		}
		for j := 0; j < 4; j++ {
			dp[n-1][j] = onesCount(j)
		}
		for i := n - 2; i >= 0; i-- {
			for j := 0; j < 4; j++ {
				dp[i][j] = math.MaxInt32
				cost := onesCount(j)
				cnt := onesCount(j^gg[i]) + onesCount(gg[i+1])
				if cnt%2 == 1 {
					dp[i][j] = min(dp[i][j], cost+dp[i+1][0])
					dp[i][j] = min(dp[i][j], cost+dp[i+1][3])
				} else {
					dp[i][j] = min(dp[i][j], cost+dp[i+1][1])
					dp[i][j] = min(dp[i][j], cost+dp[i+1][2])
				}
			}
		}
		return min(min(dp[0][0], dp[0][1]), min(dp[0][2], dp[0][3]))
	}
	solve3 := func() int {
		dp := make([][]int, n)
		for i := 0; i < n; i++ {
			dp[i] = make([]int, 8)
		}
		for j := 0; j < 8; j++ {
			dp[n-1][j] = onesCount(j)
		}
		for i := n - 2; i >= 0; i-- {
			for j := 0; j < 8; j++ {
				dp[i][j] = math.MaxInt32
				cost := onesCount(j)
				cnt1 := onesCount((j^gg[i])&3) + onesCount(gg[i+1]&3)
				cnt2 := onesCount((j^gg[i])&6) + onesCount(gg[i+1]&6)
				cnt1 %= 2
				cnt2 %= 2
				switch {
				case cnt1 == 1 && cnt2 == 1:
					dp[i][j] = min(dp[i][j], cost+dp[i+1][0])
					dp[i][j] = min(dp[i][j], cost+dp[i+1][7])
				case cnt1 == 1 && cnt2 == 0:
					dp[i][j] = min(dp[i][j], cost+dp[i+1][3])
					dp[i][j] = min(dp[i][j], cost+dp[i+1][4])
				case cnt1 == 0 && cnt2 == 1:
					dp[i][j] = min(dp[i][j], cost+dp[i+1][1])
					dp[i][j] = min(dp[i][j], cost+dp[i+1][6])
				default:
					dp[i][j] = min(dp[i][j], cost+dp[i+1][2])
					dp[i][j] = min(dp[i][j], cost+dp[i+1][5])
				}
			}
		}
		mn := math.MaxInt32
		for j := 0; j < 8; j++ {
			mn = min(mn, dp[0][j])
		}
		return mn
	}

	makegg := func() {
		gg = make([]int, n)
		for i, row := range g {
			for j, x := range row {
				gg[i] |= x << j
			}
		}
	}

	ans := -1
	if m == 1 {
		ans = 0
	} else if m == 2 {
		makegg()
		ans = solve2()
	} else if m == 3 {
		makegg()
		ans = solve3()
	}

	io.Println(ans)
}

type FastIO struct {
	*bufio.Reader
	*bufio.Writer
	c [1]byte
}

func NewFastIO(r io.Reader, w io.Writer) *FastIO {
	return &FastIO{Reader: bufio.NewReader(r), Writer: bufio.NewWriter(w)}
}

func (f *FastIO) readC() byte {
	f.Read(f.c[:])
	return f.c[0]
}

func (f *FastIO) NextInt() int {
	c := f.readC()
	for ; c <= ' '; c = f.readC() {
	}
	neg := false
	if c == '-' {
		neg = true
		c = f.readC()
	}
	n := 0
	for ; '0' <= c && c <= '9'; c = f.readC() {
		n = n*10 + int(c) - '0'
	}
	if neg {
		return -n
	}
	return n
}

func (f *FastIO) NextInts(n int) []int {
	a := make([]int, n)
	for i := 0; i < n; i++ {
		a[i] = f.NextInt()
	}
	return a
}

func (f *FastIO) Next() string {
	c := f.readC()
	for ; c <= ' '; c = f.readC() {
	}
	var s []byte
	for ; c > ' '; c = f.readC() {
		s = append(s, f.c[0])
	}
	return string(s)
}

func (f *FastIO) Println(a ...interface{}) {
	fmt.Fprintln(f.Writer, a...)
}

func (f *FastIO) Print(a ...interface{}) {
	fmt.Fprint(f.Writer, a...)
}

func (f *FastIO) PrintInts(a ...int) {
	if len(a) > 0 {
		f.Print(a[0])
		for _, x := range a[1:] {
			f.Print(" ", x)
		}
	}
}

func (f *FastIO) PrintlnInts(a ...int) {
	f.PrintInts(a...)
	f.Println()
}

package main

import (
	"bufio"
	"fmt"
	"io"
	"math"
	"math/bits"
	"os"
)

func main() {
	io := NewFastIO(os.Stdin, os.Stdout)
	defer io.Flush()

	n, m := io.NextInt(), io.NextInt()
	g := make([]int, n)
	if n < m {
		g = make([]int, m)
	}
	for i := 0; i < n; i++ {
		s := io.Next()
		for j, c := range s {
			ii := i
			if n < m {
				ii, j = j, ii
			}
			g[ii] |= int(c-'0') << j
		}
	}
	if n < m {
		n, m = m, n
	}

	onesCount := func(x int) int {
		return bits.OnesCount(uint(x))
	}

	ans := -1
	if m == 1 {
		ans = 0
	} else if m == 2 || m == 3 {
		dp := make([][]int, n)
		mm := 1 << m
		dp[n-1] = make([]int, mm)
		for j := 0; j < mm; j++ {
			dp[n-1][j] = onesCount(j)
		}
		for i := n - 2; i >= 0; i-- {
			dp[i] = make([]int, mm)
			for j := 0; j < mm; j++ {
				cost := onesCount(j)
				dp[i][j] = math.MaxInt32
				setDP := func(k int) {
					if cost+dp[i+1][k] < dp[i][j] {
						dp[i][j] = cost + dp[i+1][k]
					}
				}
				if m == 2 {
					cnt := onesCount(j^g[i]) + onesCount(g[i+1])
					if cnt%2 == 0 {
						setDP(1)
						setDP(2)
					} else {
						setDP(0)
						setDP(3)
					}
				} else {
					cnt0 := onesCount((j^g[i])&3) + onesCount(g[i+1]&3)
					cnt1 := onesCount((j^g[i])&6) + onesCount(g[i+1]&6)
					cnt := cnt0%2 | cnt1%2<<1
					switch cnt {
					case 0:
						setDP(2)
						setDP(5)
					case 1:
						setDP(3)
						setDP(4)
					case 2:
						setDP(1)
						setDP(6)
					case 3:
						setDP(0)
						setDP(7)
					}
				}
			}
		}
		ans = math.MaxInt32
		for j := 0; j < mm; j++ {
			if dp[0][j] < ans {
				ans = dp[0][j]
			}
		}
	}

	io.Println(ans)
}

type FastIO struct {
	*bufio.Reader
	*bufio.Writer
	c [1]byte
}

func NewFastIO(r io.Reader, w io.Writer) *FastIO {
	return &FastIO{Reader: bufio.NewReader(r), Writer: bufio.NewWriter(w)}
}

func (f *FastIO) readC() byte {
	f.Read(f.c[:])
	return f.c[0]
}

func (f *FastIO) NextInt() int {
	c := f.readC()
	for ; c <= ' '; c = f.readC() {
	}
	neg := false
	if c == '-' {
		neg = true
		c = f.readC()
	}
	n := 0
	for ; '0' <= c && c <= '9'; c = f.readC() {
		n = n*10 + int(c) - '0'
	}
	if neg {
		return -n
	}
	return n
}

func (f *FastIO) Next() string {
	c := f.readC()
	for ; c <= ' '; c = f.readC() {
	}
	var s []byte
	for ; c > ' '; c = f.readC() {
		s = append(s, f.c[0])
	}
	return string(s)
}

func (f *FastIO) Println(a ...interface{}) {
	fmt.Fprintln(f.Writer, a...)
}

package main

import (
	"bufio"
	"fmt"
	"io"
	"math"
	"math/bits"
	"os"
)

func main() {
	io := NewFastIO(os.Stdin, os.Stdout)
	defer io.Flush()

	n, m := io.NextInt(), io.NextInt()
	g := make([]int, n)
	if n < m {
		g = make([]int, m)
	}
	for i := 0; i < n; i++ {
		s := io.Next()
		for j, c := range s {
			ii := i
			if n < m {
				ii, j = j, ii
			}
			g[ii] |= int(c-'0') << j
		}
	}
	if n < m {
		n, m = m, n
	}

	onesCount := func(x int) int {
		return bits.OnesCount(uint(x))
	}
	min := func(i, j int) int {
		if i < j {
			return i
		}
		return j
	}

	ans := -1
	if m == 1 {
		ans = 0
	} else if m == 2 || m == 3 {
		var mp [][]int
		if m == 2 {
			mp = [][]int{{1, 2}, {0, 3}}
		} else {
			mp = [][]int{{2, 5}, {3, 4}, {1, 6}, {0, 7}}
		}
		dp := make([][]int, n)
		mm := 1 << m
		dp[n-1] = make([]int, mm)
		for j := 0; j < mm; j++ {
			dp[n-1][j] = onesCount(j)
		}
		for i := n - 2; i >= 0; i-- {
			dp[i] = make([]int, mm)
			for j := 0; j < mm; j++ {
				var cnt int
				if m == 2 {
					cnt = (onesCount(j^g[i]) + onesCount(g[i+1])) % 2
				} else {
					cnt0 := onesCount((j^g[i])&3) + onesCount(g[i+1]&3)
					cnt1 := onesCount((j^g[i])&6) + onesCount(g[i+1]&6)
					cnt = cnt0%2 | cnt1%2<<1
				}
				dp[i][j] = onesCount(j) + min(dp[i+1][mp[cnt][0]], dp[i+1][mp[cnt][1]])
			}
		}
		ans = math.MaxInt32
		for j := 0; j < mm; j++ {
			ans = min(ans, dp[0][j])
		}
	}

	io.Println(ans)
}

type FastIO struct {
	*bufio.Reader
	*bufio.Writer
	c [1]byte
}

func NewFastIO(r io.Reader, w io.Writer) *FastIO {
	return &FastIO{Reader: bufio.NewReader(r), Writer: bufio.NewWriter(w)}
}

func (f *FastIO) readC() byte {
	f.Read(f.c[:])
	return f.c[0]
}

func (f *FastIO) NextInt() int {
	c := f.readC()
	for ; c <= ' '; c = f.readC() {
	}
	neg := false
	if c == '-' {
		neg = true
		c = f.readC()
	}
	n := 0
	for ; '0' <= c && c <= '9'; c = f.readC() {
		n = n*10 + int(c) - '0'
	}
	if neg {
		return -n
	}
	return n
}

func (f *FastIO) Next() string {
	c := f.readC()
	for ; c <= ' '; c = f.readC() {
	}
	var s []byte
	for ; c > ' '; c = f.readC() {
		s = append(s, f.c[0])
	}
	return string(s)
}

func (f *FastIO) Println(a ...interface{}) {
	fmt.Fprintln(f.Writer, a...)
}

/*
URL:
https://codeforces.com/contest/1391/problem/D
*/

package main

import (
	"bufio"
	"fmt"
	"io"
	"math"
	"os"
	"strconv"
)

/*******************************************************************/

const (
	// General purpose
	MOD = 1000000000 + 7
	// MOD          = 998244353
	ALPHABET_NUM = 26
	INF_INT64    = math.MaxInt64
	INF_BIT60    = 1 << 60
	INF_INT32    = math.MaxInt32
	INF_BIT30    = 1 << 30
	NIL          = -1

	// for dijkstra, prim, and so on
	WHITE = 0
	GRAY  = 1
	BLACK = 2
)

var (
	n, m int

	R [][]rune
	M [][]int
	A []int

	dp   [1000000 + 50][8 + 10]int
	ok   [8 + 10][8 + 10]bool
	done [8 + 10][8 + 10]bool
)

func main() {
	n, m = ReadInt2()
	for i := 0; i < n; i++ {
		row := ReadRuneSlice()
		R = append(R, row)
	}

	if n >= 4 {
		fmt.Println(-1)
		return
	}
	if n == 1 {
		fmt.Println(0)
		return
	}

	M = sub(R)
	PrintfDebug("%v\n", M)

	A = subsub(M)
	PrintfDebug("%v\n", A)

	for i := 0; i < m; i++ {
		for S := 0; S < 1<<uint(n); S++ {
			dp[i][S] = INF_BIT30
		}
	}
	for S := 0; S < 1<<uint(n); S++ {
		dp[0][S] = PopCount(S ^ A[0])
	}

	for i := 1; i < m; i++ {
		for pmask := 0; pmask < 1<<uint(n); pmask++ {
			for cmask := 0; cmask < 1<<uint(n); cmask++ {
				if check(pmask, cmask) {
					ChMin(&dp[i][cmask], dp[i-1][pmask]+PopCount(cmask^A[i]))
				}
			}
		}
	}

	ans := INF_BIT30
	for S := 0; S < 1<<uint(n); S++ {
		ChMin(&ans, dp[m-1][S])
	}
	fmt.Println(ans)
}

func check(pmask, cmask int) bool {
	if done[pmask][cmask] {
		return ok[pmask][cmask]
	}

	done[pmask][cmask] = true
	for i := 0; i < n-1; i++ {
		cnt := 0
		cnt += NthBit(pmask, i)
		cnt += NthBit(cmask, i)
		cnt += NthBit(pmask, i+1)
		cnt += NthBit(cmask, i+1)

		if cnt%2 == 0 {
			ok[pmask][cmask] = false
			return ok[pmask][cmask]
		}
	}
	ok[pmask][cmask] = true
	return ok[pmask][cmask]
}

func sub(R [][]rune) [][]int {
	res := [][]int{}
	for j := 0; j < m; j++ {
		row := []int{}
		for i := 0; i < n; i++ {
			if R[i][j] == '1' {
				row = append(row, 1)
			} else {
				row = append(row, 0)
			}
		}
		res = append(res, row)
	}
	return res
}

func subsub(M [][]int) []int {
	res := []int{}
	for i := 0; i < len(M); i++ {
		mask := 0
		for j := 0; j < len(M[i]); j++ {
			if M[i][j] == 1 {
				mask += 1 << uint(j)
			}
		}
		res = append(res, mask)
	}
	return res
}

// NthBit returns nth bit value of an argument.
// n starts from 0.
func NthBit(num int, nth int) int {
	return num >> uint(nth) & 1
}

// OnBit returns the integer that has nth ON bit.
// If an argument has nth ON bit, OnBit returns the argument.
func OnBit(num int, nth int) int {
	return num | (1 << uint(nth))
}

// OffBit returns the integer that has nth OFF bit.
// If an argument has nth OFF bit, OffBit returns the argument.
func OffBit(num int, nth int) int {
	return num & ^(1 << uint(nth))
}

// PopCount returns the number of ON bit of an argument.
func PopCount(num int) int {
	res := 0

	// for i := 0; i < 70; i++ {
	for i := 0; i < 5; i++ {
		if ((num >> uint(i)) & 1) == 1 {
			res++
		}
	}

	return res
}

// ChMin accepts a pointer of integer and a target value.
// If target value is SMALLER than the first argument,
//	then the first argument will be updated by the second argument.
func ChMin(updatedValue *int, target int) bool {
	if *updatedValue > target {
		*updatedValue = target
		return true
	}
	return false
}

/*******************************************************************/

/********** bufio setting **********/

func init() {
	// bufio.ScanWords <---> bufio.ScanLines
	ReadString = newReadString(os.Stdin, bufio.ScanWords)
	stdout = bufio.NewWriter(os.Stdout)
}

/********** FAU standard libraries **********/

//fmt.Sprintf("%b\n", 255) 	// binary expression

/********** I/O usage **********/

//str := ReadString()
//i := ReadInt()
//X := ReadIntSlice(n)
//S := ReadRuneSlice()
//a := ReadFloat64()
//A := ReadFloat64Slice(n)

//str := ZeroPaddingRuneSlice(num, 32)
//str := PrintIntsLine(X...)

/*********** Input ***********/

var (
	// ReadString returns a WORD string.
	ReadString func() string
	stdout     *bufio.Writer
)

func newReadString(ior io.Reader, sf bufio.SplitFunc) func() string {
	r := bufio.NewScanner(ior)
	r.Buffer(make([]byte, 1024), int(1e+9)) // for Codeforces
	r.Split(sf)

	return func() string {
		if !r.Scan() {
			panic("Scan failed")
		}
		return r.Text()
	}
}

// ReadInt returns an integer.
func ReadInt() int {
	return int(readInt64())
}
func ReadInt2() (int, int) {
	return int(readInt64()), int(readInt64())
}
func ReadInt3() (int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64())
}
func ReadInt4() (int, int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64()), int(readInt64())
}

// ReadInt64 returns as integer as int64.
func ReadInt64() int64 {
	return readInt64()
}
func ReadInt64_2() (int64, int64) {
	return readInt64(), readInt64()
}
func ReadInt64_3() (int64, int64, int64) {
	return readInt64(), readInt64(), readInt64()
}
func ReadInt64_4() (int64, int64, int64, int64) {
	return readInt64(), readInt64(), readInt64(), readInt64()
}

func readInt64() int64 {
	i, err := strconv.ParseInt(ReadString(), 0, 64)
	if err != nil {
		panic(err.Error())
	}
	return i
}

// ReadIntSlice returns an integer slice that has n integers.
func ReadIntSlice(n int) []int {
	b := make([]int, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt()
	}
	return b
}

// ReadInt64Slice returns as int64 slice that has n integers.
func ReadInt64Slice(n int) []int64 {
	b := make([]int64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt64()
	}
	return b
}

// ReadFloat64 returns an float64.
func ReadFloat64() float64 {
	return float64(readFloat64())
}

func readFloat64() float64 {
	f, err := strconv.ParseFloat(ReadString(), 64)
	if err != nil {
		panic(err.Error())
	}
	return f
}

// ReadFloatSlice returns an float64 slice that has n float64.
func ReadFloat64Slice(n int) []float64 {
	b := make([]float64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadFloat64()
	}
	return b
}

// ReadRuneSlice returns a rune slice.
func ReadRuneSlice() []rune {
	return []rune(ReadString())
}

/*********** Output ***********/

// PrintIntsLine returns integers string delimited by a space.
func PrintIntsLine(A ...int) string {
	res := []rune{}

	for i := 0; i < len(A); i++ {
		str := strconv.Itoa(A[i])
		res = append(res, []rune(str)...)

		if i != len(A)-1 {
			res = append(res, ' ')
		}
	}

	return string(res)
}

// PrintIntsLine returns integers string delimited by a space.
func PrintInts64Line(A ...int64) string {
	res := []rune{}

	for i := 0; i < len(A); i++ {
		str := strconv.FormatInt(A[i], 10) // 64bit int version
		res = append(res, []rune(str)...)

		if i != len(A)-1 {
			res = append(res, ' ')
		}
	}

	return string(res)
}

// PrintfBufStdout is function for output strings to buffered os.Stdout.
// You may have to call stdout.Flush() finally.
func PrintfBufStdout(format string, a ...interface{}) {
	fmt.Fprintf(stdout, format, a...)
}

/*********** Debugging ***********/

// PrintfDebug is wrapper of fmt.Fprintf(os.Stderr, format, a...)
func PrintfDebug(format string, a ...interface{}) {
	fmt.Fprintf(os.Stderr, format, a...)
}

// ZeroPaddingRuneSlice returns binary expressions of integer n with zero padding.
// For debugging use.
func ZeroPaddingRuneSlice(n, digitsNum int) []rune {
	sn := fmt.Sprintf("%b", n)

	residualLength := digitsNum - len(sn)
	if residualLength <= 0 {
		return []rune(sn)
	}

	zeros := make([]rune, residualLength)
	for i := 0; i < len(zeros); i++ {
		zeros[i] = '0'
	}

	res := []rune{}
	res = append(res, zeros...)
	res = append(res, []rune(sn)...)

	return res
}

/*
URL:
https://codeforces.com/contest/1391/problem/D
*/

package main

import (
	"bufio"
	"fmt"
	"io"
	"math"
	"os"
	"strconv"
)

/*******************************************************************/

const (
	// General purpose
	MOD = 1000000000 + 7
	// MOD          = 998244353
	ALPHABET_NUM = 26
	INF_INT64    = math.MaxInt64
	INF_BIT60    = 1 << 60
	INF_INT32    = math.MaxInt32
	INF_BIT30    = 1 << 30
	NIL          = -1

	// for dijkstra, prim, and so on
	WHITE = 0
	GRAY  = 1
	BLACK = 2
)

var (
	n, m int

	R [][]rune
	M [][]int
	A []int

	dp   [1000000 + 50][8 + 10]int
	ok   [8 + 10][8 + 10]bool
	done [8 + 10][8 + 10]bool
)

func main() {
	n, m = ReadInt2()
	for i := 0; i < n; i++ {
		row := ReadRuneSlice()
		R = append(R, row)
	}

	if n >= 4 {
		fmt.Println(-1)
		return
	}
	if n == 1 {
		fmt.Println(0)
		return
	}

	M = sub(R)
	PrintfDebug("%v\n", M)

	A = subsub(M)
	PrintfDebug("%v\n", A)

	for i := 0; i < m; i++ {
		for S := 0; S < 1<<uint(n); S++ {
			dp[i][S] = INF_BIT30
		}
	}
	for S := 0; S < 1<<uint(n); S++ {
		dp[0][S] = PopCount(S^A[0], n)
	}

	for i := 1; i < m; i++ {
		for pmask := 0; pmask < 1<<uint(n); pmask++ {
			for cmask := 0; cmask < 1<<uint(n); cmask++ {
				if check(pmask, cmask) {
					ChMin(&dp[i][cmask], dp[i-1][pmask]+PopCount(cmask^A[i], n))
				}
			}
		}
	}

	ans := INF_BIT30
	for S := 0; S < 1<<uint(n); S++ {
		ChMin(&ans, dp[m-1][S])
	}
	fmt.Println(ans)
}

func check(pmask, cmask int) bool {
	if done[pmask][cmask] {
		return ok[pmask][cmask]
	}

	done[pmask][cmask] = true
	for i := 0; i < n-1; i++ {
		cnt := 0
		cnt += NthBit(pmask, i)
		cnt += NthBit(cmask, i)
		cnt += NthBit(pmask, i+1)
		cnt += NthBit(cmask, i+1)

		if cnt%2 == 0 {
			ok[pmask][cmask] = false
			return ok[pmask][cmask]
		}
	}
	ok[pmask][cmask] = true
	return ok[pmask][cmask]
}

func sub(R [][]rune) [][]int {
	res := [][]int{}
	for j := 0; j < m; j++ {
		row := []int{}
		for i := 0; i < n; i++ {
			if R[i][j] == '1' {
				row = append(row, 1)
			} else {
				row = append(row, 0)
			}
		}
		res = append(res, row)
	}
	return res
}

func subsub(M [][]int) []int {
	res := []int{}
	for i := 0; i < len(M); i++ {
		mask := 0
		for j := 0; j < len(M[i]); j++ {
			if M[i][j] == 1 {
				mask += 1 << uint(j)
			}
		}
		res = append(res, mask)
	}
	return res
}

// NthBit returns nth bit value of an argument.
// n starts from 0.
func NthBit(num int, nth int) int {
	return num >> uint(nth) & 1
}

// OnBit returns the integer that has nth ON bit.
// If an argument has nth ON bit, OnBit returns the argument.
func OnBit(num int, nth int) int {
	return num | (1 << uint(nth))
}

// OffBit returns the integer that has nth OFF bit.
// If an argument has nth OFF bit, OffBit returns the argument.
func OffBit(num int, nth int) int {
	return num & ^(1 << uint(nth))
}

// PopCount returns the number of ON bit of an argument.
func PopCount(num int, ub int) int {
	res := 0

	for i := 0; i < ub; i++ {
		if ((num >> uint(i)) & 1) == 1 {
			res++
		}
	}

	return res
}

// ChMin accepts a pointer of integer and a target value.
// If target value is SMALLER than the first argument,
//	then the first argument will be updated by the second argument.
func ChMin(updatedValue *int, target int) bool {
	if *updatedValue > target {
		*updatedValue = target
		return true
	}
	return false
}

/*******************************************************************/

/********** bufio setting **********/

func init() {
	// bufio.ScanWords <---> bufio.ScanLines
	ReadString = newReadString(os.Stdin, bufio.ScanWords)
	stdout = bufio.NewWriter(os.Stdout)
}

/********** FAU standard libraries **********/

//fmt.Sprintf("%b\n", 255) 	// binary expression

/********** I/O usage **********/

//str := ReadString()
//i := ReadInt()
//X := ReadIntSlice(n)
//S := ReadRuneSlice()
//a := ReadFloat64()
//A := ReadFloat64Slice(n)

//str := ZeroPaddingRuneSlice(num, 32)
//str := PrintIntsLine(X...)

/*********** Input ***********/

var (
	// ReadString returns a WORD string.
	ReadString func() string
	stdout     *bufio.Writer
)

func newReadString(ior io.Reader, sf bufio.SplitFunc) func() string {
	r := bufio.NewScanner(ior)
	r.Buffer(make([]byte, 1024), int(1e+9)) // for Codeforces
	r.Split(sf)

	return func() string {
		if !r.Scan() {
			panic("Scan failed")
		}
		return r.Text()
	}
}

// ReadInt returns an integer.
func ReadInt() int {
	return int(readInt64())
}
func ReadInt2() (int, int) {
	return int(readInt64()), int(readInt64())
}
func ReadInt3() (int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64())
}
func ReadInt4() (int, int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64()), int(readInt64())
}

// ReadInt64 returns as integer as int64.
func ReadInt64() int64 {
	return readInt64()
}
func ReadInt64_2() (int64, int64) {
	return readInt64(), readInt64()
}
func ReadInt64_3() (int64, int64, int64) {
	return readInt64(), readInt64(), readInt64()
}
func ReadInt64_4() (int64, int64, int64, int64) {
	return readInt64(), readInt64(), readInt64(), readInt64()
}

func readInt64() int64 {
	i, err := strconv.ParseInt(ReadString(), 0, 64)
	if err != nil {
		panic(err.Error())
	}
	return i
}

// ReadIntSlice returns an integer slice that has n integers.
func ReadIntSlice(n int) []int {
	b := make([]int, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt()
	}
	return b
}

// ReadInt64Slice returns as int64 slice that has n integers.
func ReadInt64Slice(n int) []int64 {
	b := make([]int64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt64()
	}
	return b
}

// ReadFloat64 returns an float64.
func ReadFloat64() float64 {
	return float64(readFloat64())
}

func readFloat64() float64 {
	f, err := strconv.ParseFloat(ReadString(), 64)
	if err != nil {
		panic(err.Error())
	}
	return f
}

// ReadFloatSlice returns an float64 slice that has n float64.
func ReadFloat64Slice(n int) []float64 {
	b := make([]float64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadFloat64()
	}
	return b
}

// ReadRuneSlice returns a rune slice.
func ReadRuneSlice() []rune {
	return []rune(ReadString())
}

/*********** Output ***********/

// PrintIntsLine returns integers string delimited by a space.
func PrintIntsLine(A ...int) string {
	res := []rune{}

	for i := 0; i < len(A); i++ {
		str := strconv.Itoa(A[i])
		res = append(res, []rune(str)...)

		if i != len(A)-1 {
			res = append(res, ' ')
		}
	}

	return string(res)
}

// PrintIntsLine returns integers string delimited by a space.
func PrintInts64Line(A ...int64) string {
	res := []rune{}

	for i := 0; i < len(A); i++ {
		str := strconv.FormatInt(A[i], 10) // 64bit int version
		res = append(res, []rune(str)...)

		if i != len(A)-1 {
			res = append(res, ' ')
		}
	}

	return string(res)
}

// PrintfBufStdout is function for output strings to buffered os.Stdout.
// You may have to call stdout.Flush() finally.
func PrintfBufStdout(format string, a ...interface{}) {
	fmt.Fprintf(stdout, format, a...)
}

/*********** Debugging ***********/

// PrintfDebug is wrapper of fmt.Fprintf(os.Stderr, format, a...)
func PrintfDebug(format string, a ...interface{}) {
	fmt.Fprintf(os.Stderr, format, a...)
}

// ZeroPaddingRuneSlice returns binary expressions of integer n with zero padding.
// For debugging use.
func ZeroPaddingRuneSlice(n, digitsNum int) []rune {
	sn := fmt.Sprintf("%b", n)

	residualLength := digitsNum - len(sn)
	if residualLength <= 0 {
		return []rune(sn)
	}

	zeros := make([]rune, residualLength)
	for i := 0; i < len(zeros); i++ {
		zeros[i] = '0'
	}

	res := []rune{}
	res = append(res, zeros...)
	res = append(res, []rune(sn)...)

	return res
}

package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
)

var in *bufio.Reader
var out *bufio.Writer

func printf(x string, a ...interface{}) (n int, err error) {
	return fmt.Fprintf(out, x, a...)
}

func scanf(x string, a ...interface{}) (n int, err error) {
	return fmt.Fscanf(in, x, a...)
}

type priorityQueue [][2]int

func (pq priorityQueue) Len() int {
	return len(pq)
}

func (pq priorityQueue) Less(i, j int) bool {
	return pq[i][1] > pq[j][1]
}

func (pq priorityQueue) Swap(i, j int) {
	pq[i], pq[j] = pq[j], pq[i]
}

func (pq *priorityQueue) Push(x interface{}) {
	*pq = append(*pq, x.([2]int))
}

func (pq *priorityQueue) Pop() (res interface{}) {
	res = (*pq)[len(*pq)-1]
	*pq = (*pq)[:len(*pq)-1]
	return res
}

var err error

func next() (n string) {
	for {
		_, err = scanf("%v", &n)
		if err == nil {
			return n
		}
		if err.Error() == "EOF" {
			return ""
		}
	}
}

func nextInt() (n int) {
	for {
		_, err = scanf("%v", &n)
		if err == nil {
			return n
		}
		if err.Error() == "EOF" {
			return 0
		}
	}
}

func nextLong() (n int64) {
	for {
		_, err = scanf("%v", &n)
		if err == nil {
			return n
		}
		if err.Error() == "EOF" {
			return 0
		}
	}
}

func nextDouble() (n float64) {
	for {
		_, err = scanf("%v", &n)
		if err == nil {
			return n
		}
		if err.Error() == "EOF" {
			return 0
		}
	}
}

func Fill(a []int, v int) {
	for i := range a {
		a[i] = v
	}
}

func FillB(a []bool, v bool) {
	for i := range a {
		a[i] = v
	}
}

func Min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func Max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

var (
	dx [4]int = [4]int{-1, 0, 1, 0}
	dy [4]int = [4]int{0, 1, 0, -1}
)

const (
	mod = 1000000007
)

func mult(a, b int64) int64 {
	return (a * b) % mod
}
func add(a, b int64) int64 {
	return (a + b) % mod
}

func sub(a, b int64) int64 {
	return (a - b + mod) % mod
}

func solve() {
	n, m := nextInt(), nextInt()
	if n >= 4 {
		printf("-1\n")
		return
	}
	arr := make([][]byte, n)
	for i := range arr {
		arr[i] = []byte(next())
		for j := range arr[i] {
			arr[i][j] -= '0'
		}
	}
	dp := make([][]int, 1<<n)
	for i := range dp {
		dp[i] = make([]int, m+1)
	}
	for i := m - 1; i >= 0; i-- {
		for j := 0; j < 1<<n; j++ {
			dp[j][i] = n * m
		outer:
			for k := 0; k < 1<<n; k++ {
				if i != 0 {
					for l := 0; l < n-1; l++ {
						if !((j&(1<<l) != 0) != (j&(1<<(l+1)) != 0) != (k&(1<<l) != 0) != (k&(1<<(l+1)) != 0)) {
							continue outer
						}
					}
				}
				res := dp[j][i+1]
				for l := 0; l < n-1; l++ {
					if (k&(1<<l) != 0) != (arr[l][i] != 0) {
						res++
					}
				}
				if res < dp[j][i] {
					dp[j][i] = res
				}
			}
		}
	}
	printf("%d\n", dp[0][0])
}

func main() {
	in = bufio.NewReaderSize(os.Stdin, 8192)
	out = bufio.NewWriterSize(os.Stdout, 8192)
	if false {
		name := "in"
		file1, err := os.Open(name + ".in")
		if err != nil {
			log.Fatal(err)
		}
		// file2, err := os.Create(name + ".out")
		// if err != nil {
		// 	log.Fatal(err)
		// }
		in = bufio.NewReaderSize(file1, 8192)
		// out = bufio.NewWriterSize(file2, 8192)
	}
	defer out.Flush()

	// for t := nextInt(); t > 0; t-- {
	solve()
	// }
}

package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
)

var in *bufio.Reader
var out *bufio.Writer

func printf(x string, a ...interface{}) (n int, err error) {
	return fmt.Fprintf(out, x, a...)
}

func scanf(x string, a ...interface{}) (n int, err error) {
	return fmt.Fscanf(in, x, a...)
}

type priorityQueue [][2]int

func (pq priorityQueue) Len() int {
	return len(pq)
}

func (pq priorityQueue) Less(i, j int) bool {
	return pq[i][1] > pq[j][1]
}

func (pq priorityQueue) Swap(i, j int) {
	pq[i], pq[j] = pq[j], pq[i]
}

func (pq *priorityQueue) Push(x interface{}) {
	*pq = append(*pq, x.([2]int))
}

func (pq *priorityQueue) Pop() (res interface{}) {
	res = (*pq)[len(*pq)-1]
	*pq = (*pq)[:len(*pq)-1]
	return res
}

var err error

func next() (n string) {
	for {
		_, err = scanf("%v", &n)
		if err == nil {
			return n
		}
		if err.Error() == "EOF" {
			return ""
		}
	}
}

func nextInt() (n int) {
	for {
		_, err = scanf("%v", &n)
		if err == nil {
			return n
		}
		if err.Error() == "EOF" {
			return 0
		}
	}
}

func nextLong() (n int64) {
	for {
		_, err = scanf("%v", &n)
		if err == nil {
			return n
		}
		if err.Error() == "EOF" {
			return 0
		}
	}
}

func nextDouble() (n float64) {
	for {
		_, err = scanf("%v", &n)
		if err == nil {
			return n
		}
		if err.Error() == "EOF" {
			return 0
		}
	}
}

func Fill(a []int, v int) {
	for i := range a {
		a[i] = v
	}
}

func FillB(a []bool, v bool) {
	for i := range a {
		a[i] = v
	}
}

func Min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func Max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

var (
	dx [4]int = [4]int{-1, 0, 1, 0}
	dy [4]int = [4]int{0, 1, 0, -1}
)

const (
	mod = 1000000007
)

func mult(a, b int64) int64 {
	return (a * b) % mod
}
func add(a, b int64) int64 {
	return (a + b) % mod
}

func sub(a, b int64) int64 {
	return (a - b + mod) % mod
}

func solve() {
	n, m := nextInt(), nextInt()
	if n >= 4 && m >= 4 {
		printf("-1\n")
		return
	}
	arr := make([][]byte, n)
	for i := range arr {
		arr[i] = []byte(next())
		for j := range arr[i] {
			arr[i][j] -= '0'
		}
	}
	res := n * m
outer:
	for mask := 0; mask < 1<<(n*m); mask++ {
		build := make([][]byte, n)
		tot := 0
		for i := range build {
			build[i] = make([]byte, m)
			for j := range build[i] {
				if mask&(1<<(m*i+j)) > 0 {
					build[i][j] = 1
				}
				if build[i][j] != arr[i][j] {
					tot++
				}
			}
		}
		for i := 0; i < n-1; i++ {
			for j := 0; j < m-1; j++ {
				if (build[i][j]+build[i+1][j]+build[i][j+1]+build[i+1][j+1])&1 == 0 {
					continue outer
				}
			}
		}
		if tot < res {
			res = tot
		}
	}
	printf("%d\n", res)
}

func main() {
	in = bufio.NewReaderSize(os.Stdin, 8192)
	out = bufio.NewWriterSize(os.Stdout, 8192)
	if false {
		name := "in"
		file1, err := os.Open(name + ".in")
		if err != nil {
			log.Fatal(err)
		}
		// file2, err := os.Create(name + ".out")
		// if err != nil {
		// 	log.Fatal(err)
		// }
		in = bufio.NewReaderSize(file1, 8192)
		// out = bufio.NewWriterSize(file2, 8192)
	}
	defer out.Flush()

	// for t := nextInt(); t > 0; t-- {
	solve()
	// }
}

package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
)

var in *bufio.Reader
var out *bufio.Writer

func printf(x string, a ...interface{}) (n int, err error) {
	return fmt.Fprintf(out, x, a...)
}

func scanf(x string, a ...interface{}) (n int, err error) {
	return fmt.Fscanf(in, x, a...)
}

type priorityQueue [][2]int

func (pq priorityQueue) Len() int {
	return len(pq)
}

func (pq priorityQueue) Less(i, j int) bool {
	return pq[i][1] > pq[j][1]
}

func (pq priorityQueue) Swap(i, j int) {
	pq[i], pq[j] = pq[j], pq[i]
}

func (pq *priorityQueue) Push(x interface{}) {
	*pq = append(*pq, x.([2]int))
}

func (pq *priorityQueue) Pop() (res interface{}) {
	res = (*pq)[len(*pq)-1]
	*pq = (*pq)[:len(*pq)-1]
	return res
}

var err error

func next() (n string) {
	for {
		_, err = scanf("%v", &n)
		if err == nil {
			return n
		}
		if err.Error() == "EOF" {
			return ""
		}
	}
}

func nextInt() (n int) {
	for {
		_, err = scanf("%v", &n)
		if err == nil {
			return n
		}
		if err.Error() == "EOF" {
			return 0
		}
	}
}

func nextLong() (n int64) {
	for {
		_, err = scanf("%v", &n)
		if err == nil {
			return n
		}
		if err.Error() == "EOF" {
			return 0
		}
	}
}

func nextDouble() (n float64) {
	for {
		_, err = scanf("%v", &n)
		if err == nil {
			return n
		}
		if err.Error() == "EOF" {
			return 0
		}
	}
}

func Fill(a []int, v int) {
	for i := range a {
		a[i] = v
	}
}

func FillB(a []bool, v bool) {
	for i := range a {
		a[i] = v
	}
}

func Min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func Max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

var (
	dx [4]int = [4]int{-1, 0, 1, 0}
	dy [4]int = [4]int{0, 1, 0, -1}
)

const (
	mod = 1000000007
)

func mult(a, b int64) int64 {
	return (a * b) % mod
}
func add(a, b int64) int64 {
	return (a + b) % mod
}

func sub(a, b int64) int64 {
	return (a - b + mod) % mod
}

func solve() {
	n, m := nextInt(), nextInt()
	if n >= 4 {
		printf("-1\n")
		return
	}
	arr := make([][]byte, n)
	for i := range arr {
		arr[i] = []byte(next())
		for j := range arr[i] {
			arr[i][j] -= '0'
		}
	}
	dp := make([][]int, 1<<n)
	for i := range dp {
		dp[i] = make([]int, m+1)
	}
	for i := m - 1; i >= 0; i-- {
		for j := 0; j < 1<<n; j++ {
			dp[j][i] = n * m
		outer:
			for k := 0; k < 1<<n; k++ {
				if i != 0 {
					for l := 0; l < n-1; l++ {
						if !((j&(1<<l) != 0) != (j&(1<<(l+1)) != 0) != (k&(1<<l) != 0) != (k&(1<<(l+1)) != 0)) {
							continue outer
						}
					}
				}
				res := dp[j][i+1]
				for l := 0; l < n; l++ {
					if (k&(1<<l) != 0) != (arr[l][i] != 0) {
						res++
					}
				}
				if res < dp[j][i] {
					dp[j][i] = res
				}
			}
		}
	}
	printf("%d\n", dp[0][0])
}

func main() {
	in = bufio.NewReaderSize(os.Stdin, 8192)
	out = bufio.NewWriterSize(os.Stdout, 8192)
	if false {
		name := "in"
		file1, err := os.Open(name + ".in")
		if err != nil {
			log.Fatal(err)
		}
		// file2, err := os.Create(name + ".out")
		// if err != nil {
		// 	log.Fatal(err)
		// }
		in = bufio.NewReaderSize(file1, 8192)
		// out = bufio.NewWriterSize(file2, 8192)
	}
	defer out.Flush()

	// for t := nextInt(); t > 0; t-- {
	solve()
	// }
}

package main

import (
	"bufio"
	"fmt"
	"io"
	"math"
	"math/bits"
	"os"
)

func main() {
	io := NewFastIO(os.Stdin, os.Stdout)
	defer io.Flush()

	n, m := io.NextInt(), io.NextInt()
	g := make([]int, m)
	for i := 0; i < n; i++ {
		s := io.Next()
		for j, c := range s {
			if n < m {
				i, j = j, i
			}
			g[i] |= int(c-'0') << j
		}
	}
	if n < m {
		n, m = m, n
	}

	onesCount := func(x int) int {
		return bits.OnesCount(uint(x))
	}
	min := func(i, j int) int {
		if i < j {
			return i
		}
		return j
	}

	ans := -1
	if m == 1 {
		ans = 0
	} else {
		dp := make([][]int, n)
		mm := 1 << m
		for i := 0; i < n; i++ {
			dp[i] = make([]int, mm)
		}
		for j := 0; j < mm; j++ {
			dp[n-1][j] = onesCount(j)
		}
		for i := n - 2; i >= 0; i-- {
			for j := 0; j < mm; j++ {
				cost := onesCount(j)
				dp[i][j] = math.MaxInt32
				setDP := func(k int) {
					dp[i][j] = min(dp[i][j], cost+dp[i+1][k])
				}
				if m == 2 {
					cnt := onesCount(j^g[i]) + onesCount(g[i+1])
					if cnt%2 == 0 {
						setDP(1)
						setDP(2)
					} else {
						setDP(0)
						setDP(3)
					}
				} else {
					cnt0 := onesCount((j^g[i])&3) + onesCount(g[i+1]&3)
					cnt1 := onesCount((j^g[i])&6) + onesCount(g[i+1]&6)
					cnt := cnt0%2 | cnt1%2<<1
					switch cnt {
					case 0:
						setDP(2)
						setDP(5)
					case 1:
						setDP(1)
						setDP(6)
					case 2:
						setDP(3)
						setDP(4)
					case 3:
						setDP(0)
						setDP(7)
					}
				}
			}
		}
		ans = math.MaxInt32
		for j := 0; j < mm; j++ {
			ans = min(ans, dp[0][j])
		}
	}

	io.Println(ans)
}

type FastIO struct {
	*bufio.Reader
	*bufio.Writer
	c [1]byte
}

func NewFastIO(r io.Reader, w io.Writer) *FastIO {
	return &FastIO{Reader: bufio.NewReader(r), Writer: bufio.NewWriter(w)}
}

func (f *FastIO) readC() byte {
	f.Read(f.c[:])
	return f.c[0]
}

func (f *FastIO) NextInt() int {
	c := f.readC()
	for ; c <= ' '; c = f.readC() {
	}
	neg := false
	if c == '-' {
		neg = true
		c = f.readC()
	}
	n := 0
	for ; '0' <= c && c <= '9'; c = f.readC() {
		n = n*10 + int(c) - '0'
	}
	if neg {
		return -n
	}
	return n
}

func (f *FastIO) NextInts(n int) []int {
	a := make([]int, n)
	for i := 0; i < n; i++ {
		a[i] = f.NextInt()
	}
	return a
}

func (f *FastIO) Next() string {
	c := f.readC()
	for ; c <= ' '; c = f.readC() {
	}
	var s []byte
	for ; c > ' '; c = f.readC() {
		s = append(s, f.c[0])
	}
	return string(s)
}

func (f *FastIO) Println(a ...interface{}) {
	fmt.Fprintln(f.Writer, a...)
}

func (f *FastIO) Print(a ...interface{}) {
	fmt.Fprint(f.Writer, a...)
}

func (f *FastIO) PrintInts(a ...int) {
	if len(a) > 0 {
		f.Print(a[0])
		for _, x := range a[1:] {
			f.Print(" ", x)
		}
	}
}

func (f *FastIO) PrintlnInts(a ...int) {
	f.PrintInts(a...)
	f.Println()
}

package main

import (
	"bufio"
	"fmt"
	"io"
	"math"
	"math/bits"
	"os"
)

func main() {
	io := NewFastIO(os.Stdin, os.Stdout)
	defer io.Flush()

	n, m := io.NextInt(), io.NextInt()
	g := make([][]int, n)
	for i := range g {
		s := io.Next()
		g[i] = make([]int, m)
		for j, c := range s {
			g[i][j] = int(c - '0')
		}
	}
	if n < m {
		g2 := make([][]int, m)
		for i := 0; i < m; i++ {
			g2[i] = make([]int, n)
			for j := range g2[i] {
				g2[i][j] = g[j][i]
			}
		}
		n, m, g = m, n, g2
	}

	onesCount := func(x int) int {
		return bits.OnesCount(uint(x))
	}
	min := func(i, j int) int {
		if i < j {
			return i
		}
		return j
	}

	var gg []int
	solve2 := func() int {
		dp := make([][]int, n)
		for i := 0; i < n; i++ {
			dp[i] = make([]int, 4)
		}
		for j := 0; j < 4; j++ {
			dp[n-1][j] = onesCount(j)
		}
		for i := n - 2; i >= 0; i-- {
			for j := 0; j < 4; j++ {
				dp[i][j] = math.MaxInt32
				cost := onesCount(j)
				cnt := onesCount(j^gg[i]) + onesCount(gg[i+1])
				if cnt%2 == 1 {
					dp[i][j] = min(dp[i][j], cost+dp[i+1][0])
					dp[i][j] = min(dp[i][j], cost+dp[i+1][3])
				} else {
					dp[i][j] = min(dp[i][j], cost+dp[i+1][1])
					dp[i][j] = min(dp[i][j], cost+dp[i+1][2])
				}
			}
		}
		return min(min(dp[0][0], dp[0][1]), min(dp[0][2], dp[0][3]))
	}
	solve3 := func() int {
		dp := make([][]int, n)
		for i := 0; i < n; i++ {
			dp[i] = make([]int, 8)
		}
		for j := 0; j < 8; j++ {
			dp[n-1][j] = onesCount(j)
		}
		for i := n - 2; i >= 0; i-- {
			for j := 0; j < 8; j++ {
				dp[i][j] = math.MaxInt32
				cost := onesCount(j)
				cnt1 := onesCount((j^gg[i])&3) + onesCount(gg[i+1]&3)
				cnt2 := onesCount((j^gg[i])&6) + onesCount(gg[i+1]&6)
				switch {
				case cnt1 == 1 && cnt2 == 1:
					dp[i][j] = min(dp[i][j], cost+dp[i+1][0])
					dp[i][j] = min(dp[i][j], cost+dp[i+1][7])
				case cnt1 == 1 && cnt2 == 0:
					dp[i][j] = min(dp[i][j], cost+dp[i+1][3])
					dp[i][j] = min(dp[i][j], cost+dp[i+1][4])
				case cnt1 == 0 && cnt2 == 1:
					dp[i][j] = min(dp[i][j], cost+dp[i+1][1])
					dp[i][j] = min(dp[i][j], cost+dp[i+1][6])
				default:
					dp[i][j] = min(dp[i][j], cost+dp[i+1][2])
					dp[i][j] = min(dp[i][j], cost+dp[i+1][5])
				}
			}
		}
		mn := math.MaxInt32
		for j := 0; j < 8; j++ {
			mn = min(mn, dp[0][j])
		}
		return mn
	}

	makegg := func() {
		gg = make([]int, n)
		for i, row := range g {
			for j, x := range row {
				gg[i] |= x << j
			}
		}
	}

	ans := -1
	if m == 1 {
		ans = 0
	} else if m == 2 {
		makegg()
		ans = solve2()
	} else if m == 3 {
		makegg()
		ans = solve3()
	}

	io.Println(ans)
}

type FastIO struct {
	*bufio.Reader
	*bufio.Writer
	c [1]byte
}

func NewFastIO(r io.Reader, w io.Writer) *FastIO {
	return &FastIO{Reader: bufio.NewReader(r), Writer: bufio.NewWriter(w)}
}

func (f *FastIO) readC() byte {
	f.Read(f.c[:])
	return f.c[0]
}

func (f *FastIO) NextInt() int {
	c := f.readC()
	for ; c <= ' '; c = f.readC() {
	}
	neg := false
	if c == '-' {
		neg = true
		c = f.readC()
	}
	n := 0
	for ; '0' <= c && c <= '9'; c = f.readC() {
		n = n*10 + int(c) - '0'
	}
	if neg {
		return -n
	}
	return n
}

func (f *FastIO) NextInts(n int) []int {
	a := make([]int, n)
	for i := 0; i < n; i++ {
		a[i] = f.NextInt()
	}
	return a
}

func (f *FastIO) Next() string {
	c := f.readC()
	for ; c <= ' '; c = f.readC() {
	}
	var s []byte
	for ; c > ' '; c = f.readC() {
		s = append(s, f.c[0])
	}
	return string(s)
}

func (f *FastIO) Println(a ...interface{}) {
	fmt.Fprintln(f.Writer, a...)
}

func (f *FastIO) Print(a ...interface{}) {
	fmt.Fprint(f.Writer, a...)
}

func (f *FastIO) PrintInts(a ...int) {
	if len(a) > 0 {
		f.Print(a[0])
		for _, x := range a[1:] {
			f.Print(" ", x)
		}
	}
}

func (f *FastIO) PrintlnInts(a ...int) {
	f.PrintInts(a...)
	f.Println()
}

package main

import (
	"bufio"
	"fmt"
	"io"
	"math"
	"math/bits"
	"os"
)

func main() {
	io := NewFastIO(os.Stdin, os.Stdout)
	defer io.Flush()

	n, m := io.NextInt(), io.NextInt()
	g := make([]int, m)
	for i := 0; i < n; i++ {
		s := io.Next()
		for j, c := range s {
			if n < m {
				i, j = j, i
			}
			g[i] |= int(c-'0') << j
		}
	}
	if n < m {
		n, m = m, n
	}

	onesCount := func(x int) int {
		return bits.OnesCount(uint(x))
	}
	min := func(i, j int) int {
		if i < j {
			return i
		}
		return j
	}

	ans := -1
	if m == 1 {
		ans = 0
	} else if m == 2 || m == 3 {
		dp := make([][]int, n)
		mm := 1 << m
		for i := 0; i < n; i++ {
			dp[i] = make([]int, mm)
		}
		for j := 0; j < mm; j++ {
			dp[n-1][j] = onesCount(j)
		}
		for i := n - 2; i >= 0; i-- {
			for j := 0; j < mm; j++ {
				cost := onesCount(j)
				dp[i][j] = math.MaxInt32
				setDP := func(k int) {
					dp[i][j] = min(dp[i][j], cost+dp[i+1][k])
				}
				if m == 2 {
					cnt := onesCount(j^g[i]) + onesCount(g[i+1])
					if cnt%2 == 0 {
						setDP(1)
						setDP(2)
					} else {
						setDP(0)
						setDP(3)
					}
				} else {
					cnt0 := onesCount((j^g[i])&3) + onesCount(g[i+1]&3)
					cnt1 := onesCount((j^g[i])&6) + onesCount(g[i+1]&6)
					cnt := cnt0%2 | cnt1%2<<1
					switch cnt {
					case 0:
						setDP(2)
						setDP(5)
					case 1:
						setDP(1)
						setDP(6)
					case 2:
						setDP(3)
						setDP(4)
					case 3:
						setDP(0)
						setDP(7)
					}
				}
			}
		}
		ans = math.MaxInt32
		for j := 0; j < mm; j++ {
			ans = min(ans, dp[0][j])
		}
	}

	io.Println(ans)
}

type FastIO struct {
	*bufio.Reader
	*bufio.Writer
	c [1]byte
}

func NewFastIO(r io.Reader, w io.Writer) *FastIO {
	return &FastIO{Reader: bufio.NewReader(r), Writer: bufio.NewWriter(w)}
}

func (f *FastIO) readC() byte {
	f.Read(f.c[:])
	return f.c[0]
}

func (f *FastIO) NextInt() int {
	c := f.readC()
	for ; c <= ' '; c = f.readC() {
	}
	neg := false
	if c == '-' {
		neg = true
		c = f.readC()
	}
	n := 0
	for ; '0' <= c && c <= '9'; c = f.readC() {
		n = n*10 + int(c) - '0'
	}
	if neg {
		return -n
	}
	return n
}

func (f *FastIO) Next() string {
	c := f.readC()
	for ; c <= ' '; c = f.readC() {
	}
	var s []byte
	for ; c > ' '; c = f.readC() {
		s = append(s, f.c[0])
	}
	return string(s)
}

func (f *FastIO) Println(a ...interface{}) {
	fmt.Fprintln(f.Writer, a...)
}

/*
URL:
https://codeforces.com/contest/1391/problem/D
*/

package main

import (
	"bufio"
	"fmt"
	"io"
	"math"
	"os"
	"strconv"
)

/*******************************************************************/

const (
	// General purpose
	MOD = 1000000000 + 7
	// MOD          = 998244353
	ALPHABET_NUM = 26
	INF_INT64    = math.MaxInt64
	INF_BIT60    = 1 << 60
	INF_INT32    = math.MaxInt32
	INF_BIT30    = 1 << 30
	NIL          = -1

	// for dijkstra, prim, and so on
	WHITE = 0
	GRAY  = 1
	BLACK = 2
)

var (
	n, m int

	R [][]rune
	M [][]int
	A []int

	dp   [1000000 + 50][8 + 10]int
	ok   [8 + 10][8 + 10]bool
	done [8 + 10][8 + 10]bool
)

func main() {
	n, m = ReadInt2()
	for i := 0; i < n; i++ {
		row := ReadRuneSlice()
		R = append(R, row)
	}

	if n >= 4 {
		fmt.Println(-1)
		return
	}
	if n == 1 {
		fmt.Println(0)
		return
	}

	M = sub(R)
	PrintfDebug("%v\n", M)

	A = subsub(M)
	PrintfDebug("%v\n", A)

	for i := 0; i < n; i++ {
		for S := 0; S < 1<<uint(n); S++ {
			dp[i][S] = INF_BIT30
		}
	}
	for S := 0; S < 1<<uint(n); S++ {
		dp[0][S] = PopCount(S ^ A[0])
	}

	for i := 1; i < n; i++ {
		for pmask := 0; pmask < 1<<uint(n); pmask++ {
			for cmask := 0; cmask < 1<<uint(n); cmask++ {
				if check(pmask, cmask) {
					ChMin(&dp[i][cmask], dp[i-1][pmask]+PopCount(cmask^A[i]))
				}
			}
		}
	}

	ans := INF_BIT30
	for S := 0; S < 1<<uint(n); S++ {
		ChMin(&ans, dp[n-1][S])
	}
	fmt.Println(ans)
}

func check(pmask, cmask int) bool {
	// if done[pmask][cmask] {
	// 	return ok[pmask][cmask]
	// }

	done[pmask][cmask] = true
	for i := 0; i < n-1; i++ {
		cnt := 0
		cnt += NthBit(pmask, i)
		cnt += NthBit(cmask, i)
		cnt += NthBit(pmask, i+1)
		cnt += NthBit(cmask, i+1)

		if cnt%2 == 0 {
			ok[pmask][cmask] = false
			return ok[pmask][cmask]
		}
	}
	ok[pmask][cmask] = true
	return ok[pmask][cmask]
}

func sub(R [][]rune) [][]int {
	res := [][]int{}
	for j := 0; j < m; j++ {
		row := []int{}
		for i := 0; i < n; i++ {
			if R[i][j] == '1' {
				row = append(row, 1)
			} else {
				row = append(row, 0)
			}
		}
		res = append(res, row)
	}
	return res
}

func subsub(M [][]int) []int {
	res := []int{}
	for i := 0; i < len(M); i++ {
		mask := 0
		for j := 0; j < len(M[i]); j++ {
			if M[i][j] == 1 {
				mask += 1 << uint(j)
			}
		}
		res = append(res, mask)
	}
	return res
}

// NthBit returns nth bit value of an argument.
// n starts from 0.
func NthBit(num int, nth int) int {
	return num >> uint(nth) & 1
}

// OnBit returns the integer that has nth ON bit.
// If an argument has nth ON bit, OnBit returns the argument.
func OnBit(num int, nth int) int {
	return num | (1 << uint(nth))
}

// OffBit returns the integer that has nth OFF bit.
// If an argument has nth OFF bit, OffBit returns the argument.
func OffBit(num int, nth int) int {
	return num & ^(1 << uint(nth))
}

// PopCount returns the number of ON bit of an argument.
func PopCount(num int) int {
	res := 0

	for i := 0; i < 70; i++ {
		if ((num >> uint(i)) & 1) == 1 {
			res++
		}
	}

	return res
}

// ChMin accepts a pointer of integer and a target value.
// If target value is SMALLER than the first argument,
//	then the first argument will be updated by the second argument.
func ChMin(updatedValue *int, target int) bool {
	if *updatedValue > target {
		*updatedValue = target
		return true
	}
	return false
}

/*******************************************************************/

/********** bufio setting **********/

func init() {
	// bufio.ScanWords <---> bufio.ScanLines
	ReadString = newReadString(os.Stdin, bufio.ScanWords)
	stdout = bufio.NewWriter(os.Stdout)
}

/********** FAU standard libraries **********/

//fmt.Sprintf("%b\n", 255) 	// binary expression

/********** I/O usage **********/

//str := ReadString()
//i := ReadInt()
//X := ReadIntSlice(n)
//S := ReadRuneSlice()
//a := ReadFloat64()
//A := ReadFloat64Slice(n)

//str := ZeroPaddingRuneSlice(num, 32)
//str := PrintIntsLine(X...)

/*********** Input ***********/

var (
	// ReadString returns a WORD string.
	ReadString func() string
	stdout     *bufio.Writer
)

func newReadString(ior io.Reader, sf bufio.SplitFunc) func() string {
	r := bufio.NewScanner(ior)
	r.Buffer(make([]byte, 1024), int(1e+9)) // for Codeforces
	r.Split(sf)

	return func() string {
		if !r.Scan() {
			panic("Scan failed")
		}
		return r.Text()
	}
}

// ReadInt returns an integer.
func ReadInt() int {
	return int(readInt64())
}
func ReadInt2() (int, int) {
	return int(readInt64()), int(readInt64())
}
func ReadInt3() (int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64())
}
func ReadInt4() (int, int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64()), int(readInt64())
}

// ReadInt64 returns as integer as int64.
func ReadInt64() int64 {
	return readInt64()
}
func ReadInt64_2() (int64, int64) {
	return readInt64(), readInt64()
}
func ReadInt64_3() (int64, int64, int64) {
	return readInt64(), readInt64(), readInt64()
}
func ReadInt64_4() (int64, int64, int64, int64) {
	return readInt64(), readInt64(), readInt64(), readInt64()
}

func readInt64() int64 {
	i, err := strconv.ParseInt(ReadString(), 0, 64)
	if err != nil {
		panic(err.Error())
	}
	return i
}

// ReadIntSlice returns an integer slice that has n integers.
func ReadIntSlice(n int) []int {
	b := make([]int, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt()
	}
	return b
}

// ReadInt64Slice returns as int64 slice that has n integers.
func ReadInt64Slice(n int) []int64 {
	b := make([]int64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt64()
	}
	return b
}

// ReadFloat64 returns an float64.
func ReadFloat64() float64 {
	return float64(readFloat64())
}

func readFloat64() float64 {
	f, err := strconv.ParseFloat(ReadString(), 64)
	if err != nil {
		panic(err.Error())
	}
	return f
}

// ReadFloatSlice returns an float64 slice that has n float64.
func ReadFloat64Slice(n int) []float64 {
	b := make([]float64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadFloat64()
	}
	return b
}

// ReadRuneSlice returns a rune slice.
func ReadRuneSlice() []rune {
	return []rune(ReadString())
}

/*********** Output ***********/

// PrintIntsLine returns integers string delimited by a space.
func PrintIntsLine(A ...int) string {
	res := []rune{}

	for i := 0; i < len(A); i++ {
		str := strconv.Itoa(A[i])
		res = append(res, []rune(str)...)

		if i != len(A)-1 {
			res = append(res, ' ')
		}
	}

	return string(res)
}

// PrintIntsLine returns integers string delimited by a space.
func PrintInts64Line(A ...int64) string {
	res := []rune{}

	for i := 0; i < len(A); i++ {
		str := strconv.FormatInt(A[i], 10) // 64bit int version
		res = append(res, []rune(str)...)

		if i != len(A)-1 {
			res = append(res, ' ')
		}
	}

	return string(res)
}

// PrintfBufStdout is function for output strings to buffered os.Stdout.
// You may have to call stdout.Flush() finally.
func PrintfBufStdout(format string, a ...interface{}) {
	fmt.Fprintf(stdout, format, a...)
}

/*********** Debugging ***********/

// PrintfDebug is wrapper of fmt.Fprintf(os.Stderr, format, a...)
func PrintfDebug(format string, a ...interface{}) {
	fmt.Fprintf(os.Stderr, format, a...)
}

// ZeroPaddingRuneSlice returns binary expressions of integer n with zero padding.
// For debugging use.
func ZeroPaddingRuneSlice(n, digitsNum int) []rune {
	sn := fmt.Sprintf("%b", n)

	residualLength := digitsNum - len(sn)
	if residualLength <= 0 {
		return []rune(sn)
	}

	zeros := make([]rune, residualLength)
	for i := 0; i < len(zeros); i++ {
		zeros[i] = '0'
	}

	res := []rune{}
	res = append(res, zeros...)
	res = append(res, []rune(sn)...)

	return res
}

/*
URL:
https://codeforces.com/contest/1391/problem/D
*/

package main

import (
	"bufio"
	"fmt"
	"io"
	"math"
	"os"
	"strconv"
)

/*******************************************************************/

const (
	// General purpose
	MOD = 1000000000 + 7
	// MOD          = 998244353
	ALPHABET_NUM = 26
	INF_INT64    = math.MaxInt64
	INF_BIT60    = 1 << 60
	INF_INT32    = math.MaxInt32
	INF_BIT30    = 1 << 30
	NIL          = -1

	// for dijkstra, prim, and so on
	WHITE = 0
	GRAY  = 1
	BLACK = 2
)

var (
	n, m int

	R [][]rune
	M [][]int
	A []int

	dp   [1000000 + 50][8 + 10]int
	ok   [8 + 10][8 + 10]bool
	done [8 + 10][8 + 10]bool
)

func main() {
	n, m = ReadInt2()
	for i := 0; i < n; i++ {
		row := ReadRuneSlice()
		R = append(R, row)
	}

	if n >= 4 {
		fmt.Println(-1)
		return
	}
	if n == 1 {
		fmt.Println(0)
		return
	}

	M = sub(R)
	PrintfDebug("%v\n", M)

	A = subsub(M)
	PrintfDebug("%v\n", A)

	for i := 0; i < n; i++ {
		for S := 0; S < 1<<uint(n); S++ {
			dp[i][S] = INF_BIT30
		}
	}
	for S := 0; S < 1<<uint(n); S++ {
		dp[0][S] = PopCount(S ^ A[0])
	}

	for i := 1; i < n; i++ {
		for pmask := 0; pmask < 1<<uint(n); pmask++ {
			for cmask := 0; cmask < 1<<uint(n); cmask++ {
				if check(pmask, cmask) {
					ChMin(&dp[i][cmask], dp[i-1][pmask]+PopCount(cmask^A[i]))
				}
			}
		}
	}

	ans := INF_BIT30
	for S := 0; S < 1<<uint(n); S++ {
		ChMin(&ans, dp[n-1][S])
	}
	fmt.Println(ans)
}

func check(pmask, cmask int) bool {
	if done[pmask][cmask] {
		return ok[pmask][cmask]
	}

	done[pmask][cmask] = true
	for i := 0; i < n-1; i++ {
		cnt := 0
		cnt += NthBit(pmask, i)
		cnt += NthBit(cmask, i)
		cnt += NthBit(pmask, i+1)
		cnt += NthBit(cmask, i+1)

		if cnt%2 == 0 {
			// ok[pmask][cmask] = false
			return ok[pmask][cmask]
		}
	}
	ok[pmask][cmask] = true
	return ok[pmask][cmask]
}

func sub(R [][]rune) [][]int {
	res := [][]int{}
	for j := 0; j < m; j++ {
		row := []int{}
		for i := 0; i < n; i++ {
			if R[i][j] == '1' {
				row = append(row, 1)
			} else {
				row = append(row, 0)
			}
		}
		res = append(res, row)
	}
	return res
}

func subsub(M [][]int) []int {
	res := []int{}
	for i := 0; i < len(M); i++ {
		mask := 0
		for j := 0; j < len(M[i]); j++ {
			if M[i][j] == 1 {
				mask += 1 << uint(j)
			}
		}
		res = append(res, mask)
	}
	return res
}

// NthBit returns nth bit value of an argument.
// n starts from 0.
func NthBit(num int, nth int) int {
	return num >> uint(nth) & 1
}

// OnBit returns the integer that has nth ON bit.
// If an argument has nth ON bit, OnBit returns the argument.
func OnBit(num int, nth int) int {
	return num | (1 << uint(nth))
}

// OffBit returns the integer that has nth OFF bit.
// If an argument has nth OFF bit, OffBit returns the argument.
func OffBit(num int, nth int) int {
	return num & ^(1 << uint(nth))
}

// PopCount returns the number of ON bit of an argument.
func PopCount(num int) int {
	res := 0

	for i := 0; i < 70; i++ {
		if ((num >> uint(i)) & 1) == 1 {
			res++
		}
	}

	return res
}

// ChMin accepts a pointer of integer and a target value.
// If target value is SMALLER than the first argument,
//	then the first argument will be updated by the second argument.
func ChMin(updatedValue *int, target int) bool {
	if *updatedValue > target {
		*updatedValue = target
		return true
	}
	return false
}

/*******************************************************************/

/********** bufio setting **********/

func init() {
	// bufio.ScanWords <---> bufio.ScanLines
	ReadString = newReadString(os.Stdin, bufio.ScanWords)
	stdout = bufio.NewWriter(os.Stdout)
}

/********** FAU standard libraries **********/

//fmt.Sprintf("%b\n", 255) 	// binary expression

/********** I/O usage **********/

//str := ReadString()
//i := ReadInt()
//X := ReadIntSlice(n)
//S := ReadRuneSlice()
//a := ReadFloat64()
//A := ReadFloat64Slice(n)

//str := ZeroPaddingRuneSlice(num, 32)
//str := PrintIntsLine(X...)

/*********** Input ***********/

var (
	// ReadString returns a WORD string.
	ReadString func() string
	stdout     *bufio.Writer
)

func newReadString(ior io.Reader, sf bufio.SplitFunc) func() string {
	r := bufio.NewScanner(ior)
	r.Buffer(make([]byte, 1024), int(1e+9)) // for Codeforces
	r.Split(sf)

	return func() string {
		if !r.Scan() {
			panic("Scan failed")
		}
		return r.Text()
	}
}

// ReadInt returns an integer.
func ReadInt() int {
	return int(readInt64())
}
func ReadInt2() (int, int) {
	return int(readInt64()), int(readInt64())
}
func ReadInt3() (int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64())
}
func ReadInt4() (int, int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64()), int(readInt64())
}

// ReadInt64 returns as integer as int64.
func ReadInt64() int64 {
	return readInt64()
}
func ReadInt64_2() (int64, int64) {
	return readInt64(), readInt64()
}
func ReadInt64_3() (int64, int64, int64) {
	return readInt64(), readInt64(), readInt64()
}
func ReadInt64_4() (int64, int64, int64, int64) {
	return readInt64(), readInt64(), readInt64(), readInt64()
}

func readInt64() int64 {
	i, err := strconv.ParseInt(ReadString(), 0, 64)
	if err != nil {
		panic(err.Error())
	}
	return i
}

// ReadIntSlice returns an integer slice that has n integers.
func ReadIntSlice(n int) []int {
	b := make([]int, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt()
	}
	return b
}

// ReadInt64Slice returns as int64 slice that has n integers.
func ReadInt64Slice(n int) []int64 {
	b := make([]int64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt64()
	}
	return b
}

// ReadFloat64 returns an float64.
func ReadFloat64() float64 {
	return float64(readFloat64())
}

func readFloat64() float64 {
	f, err := strconv.ParseFloat(ReadString(), 64)
	if err != nil {
		panic(err.Error())
	}
	return f
}

// ReadFloatSlice returns an float64 slice that has n float64.
func ReadFloat64Slice(n int) []float64 {
	b := make([]float64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadFloat64()
	}
	return b
}

// ReadRuneSlice returns a rune slice.
func ReadRuneSlice() []rune {
	return []rune(ReadString())
}

/*********** Output ***********/

// PrintIntsLine returns integers string delimited by a space.
func PrintIntsLine(A ...int) string {
	res := []rune{}

	for i := 0; i < len(A); i++ {
		str := strconv.Itoa(A[i])
		res = append(res, []rune(str)...)

		if i != len(A)-1 {
			res = append(res, ' ')
		}
	}

	return string(res)
}

// PrintIntsLine returns integers string delimited by a space.
func PrintInts64Line(A ...int64) string {
	res := []rune{}

	for i := 0; i < len(A); i++ {
		str := strconv.FormatInt(A[i], 10) // 64bit int version
		res = append(res, []rune(str)...)

		if i != len(A)-1 {
			res = append(res, ' ')
		}
	}

	return string(res)
}

// PrintfBufStdout is function for output strings to buffered os.Stdout.
// You may have to call stdout.Flush() finally.
func PrintfBufStdout(format string, a ...interface{}) {
	fmt.Fprintf(stdout, format, a...)
}

/*********** Debugging ***********/

// PrintfDebug is wrapper of fmt.Fprintf(os.Stderr, format, a...)
func PrintfDebug(format string, a ...interface{}) {
	fmt.Fprintf(os.Stderr, format, a...)
}

// ZeroPaddingRuneSlice returns binary expressions of integer n with zero padding.
// For debugging use.
func ZeroPaddingRuneSlice(n, digitsNum int) []rune {
	sn := fmt.Sprintf("%b", n)

	residualLength := digitsNum - len(sn)
	if residualLength <= 0 {
		return []rune(sn)
	}

	zeros := make([]rune, residualLength)
	for i := 0; i < len(zeros); i++ {
		zeros[i] = '0'
	}

	res := []rune{}
	res = append(res, zeros...)
	res = append(res, []rune(sn)...)

	return res
}

/*
URL:
https://codeforces.com/contest/1391/problem/D
*/

package main

import (
	"bufio"
	"fmt"
	"io"
	"math"
	"os"
	"strconv"
)

/*******************************************************************/

const (
	// General purpose
	MOD = 1000000000 + 7
	// MOD          = 998244353
	ALPHABET_NUM = 26
	INF_INT64    = math.MaxInt64
	INF_BIT60    = 1 << 60
	INF_INT32    = math.MaxInt32
	INF_BIT30    = 1 << 30
	NIL          = -1

	// for dijkstra, prim, and so on
	WHITE = 0
	GRAY  = 1
	BLACK = 2
)

var (
	n, m int

	R [][]rune
	M [][]int
	A []int

	dp   [1000000 + 50][8 + 10]int
	ok   [8 + 10][8 + 10]bool
	done [8 + 10][8 + 10]bool
)

func main() {
	n, m = ReadInt2()
	for i := 0; i < n; i++ {
		row := ReadRuneSlice()
		R = append(R, row)
	}

	if n >= 4 {
		fmt.Println(-1)
		return
	}
	if n == 1 {
		fmt.Println(0)
		return
	}

	M = sub(R)
	PrintfDebug("%v\n", M)

	A = subsub(M)
	PrintfDebug("%v\n", A)

	for i := 0; i < m; i++ {
		for S := 0; S < 1<<uint(n); S++ {
			dp[i][S] = INF_BIT30
		}
	}
	for S := 0; S < 1<<uint(n); S++ {
		dp[0][S] = PopCount(S ^ A[0])
	}

	for i := 1; i < m; i++ {
		for pmask := 0; pmask < 1<<uint(n); pmask++ {
			for cmask := 0; cmask < 1<<uint(n); cmask++ {
				if check(pmask, cmask) {
					ChMin(&dp[i][cmask], dp[i-1][pmask]+PopCount(cmask^A[i]))
				}
			}
		}
	}

	ans := INF_BIT30
	for S := 0; S < 1<<uint(n); S++ {
		ChMin(&ans, dp[n-1][S])
	}
	fmt.Println(ans)
}

func check(pmask, cmask int) bool {
	if done[pmask][cmask] {
		return ok[pmask][cmask]
	}

	done[pmask][cmask] = true
	for i := 0; i < n-1; i++ {
		cnt := 0
		cnt += NthBit(pmask, i)
		cnt += NthBit(cmask, i)
		cnt += NthBit(pmask, i+1)
		cnt += NthBit(cmask, i+1)

		if cnt%2 == 0 {
			ok[pmask][cmask] = false
			return ok[pmask][cmask]
		}
	}
	ok[pmask][cmask] = true
	return ok[pmask][cmask]
}

func sub(R [][]rune) [][]int {
	res := [][]int{}
	for j := 0; j < m; j++ {
		row := []int{}
		for i := 0; i < n; i++ {
			if R[i][j] == '1' {
				row = append(row, 1)
			} else {
				row = append(row, 0)
			}
		}
		res = append(res, row)
	}
	return res
}

func subsub(M [][]int) []int {
	res := []int{}
	for i := 0; i < len(M); i++ {
		mask := 0
		for j := 0; j < len(M[i]); j++ {
			if M[i][j] == 1 {
				mask += 1 << uint(j)
			}
		}
		res = append(res, mask)
	}
	return res
}

// NthBit returns nth bit value of an argument.
// n starts from 0.
func NthBit(num int, nth int) int {
	return num >> uint(nth) & 1
}

// OnBit returns the integer that has nth ON bit.
// If an argument has nth ON bit, OnBit returns the argument.
func OnBit(num int, nth int) int {
	return num | (1 << uint(nth))
}

// OffBit returns the integer that has nth OFF bit.
// If an argument has nth OFF bit, OffBit returns the argument.
func OffBit(num int, nth int) int {
	return num & ^(1 << uint(nth))
}

// PopCount returns the number of ON bit of an argument.
func PopCount(num int) int {
	res := 0

	for i := 0; i < 70; i++ {
		if ((num >> uint(i)) & 1) == 1 {
			res++
		}
	}

	return res
}

// ChMin accepts a pointer of integer and a target value.
// If target value is SMALLER than the first argument,
//	then the first argument will be updated by the second argument.
func ChMin(updatedValue *int, target int) bool {
	if *updatedValue > target {
		*updatedValue = target
		return true
	}
	return false
}

/*******************************************************************/

/********** bufio setting **********/

func init() {
	// bufio.ScanWords <---> bufio.ScanLines
	ReadString = newReadString(os.Stdin, bufio.ScanWords)
	stdout = bufio.NewWriter(os.Stdout)
}

/********** FAU standard libraries **********/

//fmt.Sprintf("%b\n", 255) 	// binary expression

/********** I/O usage **********/

//str := ReadString()
//i := ReadInt()
//X := ReadIntSlice(n)
//S := ReadRuneSlice()
//a := ReadFloat64()
//A := ReadFloat64Slice(n)

//str := ZeroPaddingRuneSlice(num, 32)
//str := PrintIntsLine(X...)

/*********** Input ***********/

var (
	// ReadString returns a WORD string.
	ReadString func() string
	stdout     *bufio.Writer
)

func newReadString(ior io.Reader, sf bufio.SplitFunc) func() string {
	r := bufio.NewScanner(ior)
	r.Buffer(make([]byte, 1024), int(1e+9)) // for Codeforces
	r.Split(sf)

	return func() string {
		if !r.Scan() {
			panic("Scan failed")
		}
		return r.Text()
	}
}

// ReadInt returns an integer.
func ReadInt() int {
	return int(readInt64())
}
func ReadInt2() (int, int) {
	return int(readInt64()), int(readInt64())
}
func ReadInt3() (int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64())
}
func ReadInt4() (int, int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64()), int(readInt64())
}

// ReadInt64 returns as integer as int64.
func ReadInt64() int64 {
	return readInt64()
}
func ReadInt64_2() (int64, int64) {
	return readInt64(), readInt64()
}
func ReadInt64_3() (int64, int64, int64) {
	return readInt64(), readInt64(), readInt64()
}
func ReadInt64_4() (int64, int64, int64, int64) {
	return readInt64(), readInt64(), readInt64(), readInt64()
}

func readInt64() int64 {
	i, err := strconv.ParseInt(ReadString(), 0, 64)
	if err != nil {
		panic(err.Error())
	}
	return i
}

// ReadIntSlice returns an integer slice that has n integers.
func ReadIntSlice(n int) []int {
	b := make([]int, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt()
	}
	return b
}

// ReadInt64Slice returns as int64 slice that has n integers.
func ReadInt64Slice(n int) []int64 {
	b := make([]int64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt64()
	}
	return b
}

// ReadFloat64 returns an float64.
func ReadFloat64() float64 {
	return float64(readFloat64())
}

func readFloat64() float64 {
	f, err := strconv.ParseFloat(ReadString(), 64)
	if err != nil {
		panic(err.Error())
	}
	return f
}

// ReadFloatSlice returns an float64 slice that has n float64.
func ReadFloat64Slice(n int) []float64 {
	b := make([]float64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadFloat64()
	}
	return b
}

// ReadRuneSlice returns a rune slice.
func ReadRuneSlice() []rune {
	return []rune(ReadString())
}

/*********** Output ***********/

// PrintIntsLine returns integers string delimited by a space.
func PrintIntsLine(A ...int) string {
	res := []rune{}

	for i := 0; i < len(A); i++ {
		str := strconv.Itoa(A[i])
		res = append(res, []rune(str)...)

		if i != len(A)-1 {
			res = append(res, ' ')
		}
	}

	return string(res)
}

// PrintIntsLine returns integers string delimited by a space.
func PrintInts64Line(A ...int64) string {
	res := []rune{}

	for i := 0; i < len(A); i++ {
		str := strconv.FormatInt(A[i], 10) // 64bit int version
		res = append(res, []rune(str)...)

		if i != len(A)-1 {
			res = append(res, ' ')
		}
	}

	return string(res)
}

// PrintfBufStdout is function for output strings to buffered os.Stdout.
// You may have to call stdout.Flush() finally.
func PrintfBufStdout(format string, a ...interface{}) {
	fmt.Fprintf(stdout, format, a...)
}

/*********** Debugging ***********/

// PrintfDebug is wrapper of fmt.Fprintf(os.Stderr, format, a...)
func PrintfDebug(format string, a ...interface{}) {
	fmt.Fprintf(os.Stderr, format, a...)
}

// ZeroPaddingRuneSlice returns binary expressions of integer n with zero padding.
// For debugging use.
func ZeroPaddingRuneSlice(n, digitsNum int) []rune {
	sn := fmt.Sprintf("%b", n)

	residualLength := digitsNum - len(sn)
	if residualLength <= 0 {
		return []rune(sn)
	}

	zeros := make([]rune, residualLength)
	for i := 0; i < len(zeros); i++ {
		zeros[i] = '0'
	}

	res := []rune{}
	res = append(res, zeros...)
	res = append(res, []rune(sn)...)

	return res
}

/*
URL:
https://codeforces.com/contest/1391/problem/D
*/

package main

import (
	"bufio"
	"fmt"
	"io"
	"math"
	"os"
	"strconv"
)

/*******************************************************************/

const (
	// General purpose
	MOD = 1000000000 + 7
	// MOD          = 998244353
	ALPHABET_NUM = 26
	INF_INT64    = math.MaxInt64
	INF_BIT60    = 1 << 60
	INF_INT32    = math.MaxInt32
	INF_BIT30    = 1 << 30
	NIL          = -1

	// for dijkstra, prim, and so on
	WHITE = 0
	GRAY  = 1
	BLACK = 2
)

var (
	n, m int

	R [][]rune
	M [][]int
	A []int

	dp   [1000000 + 50][8 + 10]int
	ok   [8 + 10][8 + 10]bool
	done [8 + 10][8 + 10]bool
)

func main() {
	n, m = ReadInt2()
	for i := 0; i < n; i++ {
		row := ReadRuneSlice()
		R = append(R, row)
	}

	if n >= 4 {
		fmt.Println(-1)
		return
	}
	if n == 1 {
		fmt.Println(0)
		return
	}

	M = sub(R)
	PrintfDebug("%v\n", M)

	A = subsub(M)
	PrintfDebug("%v\n", A)

	for i := 0; i < n; i++ {
		for S := 0; S < 1<<uint(n); S++ {
			dp[i][S] = INF_BIT30
		}
	}
	for S := 0; S < 1<<uint(n); S++ {
		dp[0][S] = PopCount(S ^ A[0])
	}

	for i := 1; i < n; i++ {
		for pmask := 0; pmask < 1<<uint(n); pmask++ {
			for cmask := 0; cmask < 1<<uint(n); cmask++ {
				if check(pmask, cmask) {
					ChMin(&dp[i][cmask], dp[i-1][pmask]+PopCount(cmask^A[i]))
				}
			}
		}
	}

	ans := INF_BIT30
	for S := 0; S < 1<<uint(n); S++ {
		ChMin(&ans, dp[n-1][S])
	}
	fmt.Println(ans)
}

func check(pmask, cmask int) bool {
	if done[pmask][cmask] {
		return ok[pmask][cmask]
	}

	done[pmask][cmask] = true
	for i := 0; i < n-1; i++ {
		cnt := 0
		cnt += NthBit(pmask, i)
		cnt += NthBit(cmask, i)
		cnt += NthBit(pmask, i+1)
		cnt += NthBit(cmask, i+1)

		if cnt%2 == 0 {
			ok[pmask][cmask] = false
			return ok[pmask][cmask]
		}
	}
	ok[pmask][cmask] = true
	return ok[pmask][cmask]
}

func sub(R [][]rune) [][]int {
	res := [][]int{}
	for j := 0; j < m; j++ {
		row := []int{}
		for i := 0; i < n; i++ {
			if R[i][j] == '1' {
				row = append(row, 1)
			} else {
				row = append(row, 0)
			}
		}
		res = append(res, row)
	}
	return res
}

func subsub(M [][]int) []int {
	res := []int{}
	for i := 0; i < len(M); i++ {
		mask := 0
		for j := 0; j < len(M[i]); j++ {
			if M[i][j] == 1 {
				mask += 1 << uint(j)
			}
		}
		res = append(res, mask)
	}
	return res
}

// NthBit returns nth bit value of an argument.
// n starts from 0.
func NthBit(num int, nth int) int {
	return num >> uint(nth) & 1
}

// OnBit returns the integer that has nth ON bit.
// If an argument has nth ON bit, OnBit returns the argument.
func OnBit(num int, nth int) int {
	return num | (1 << uint(nth))
}

// OffBit returns the integer that has nth OFF bit.
// If an argument has nth OFF bit, OffBit returns the argument.
func OffBit(num int, nth int) int {
	return num & ^(1 << uint(nth))
}

// PopCount returns the number of ON bit of an argument.
func PopCount(num int) int {
	res := 0

	for i := 0; i < 70; i++ {
		if ((num >> uint(i)) & 1) == 1 {
			res++
		}
	}

	return res
}

// ChMin accepts a pointer of integer and a target value.
// If target value is SMALLER than the first argument,
//	then the first argument will be updated by the second argument.
func ChMin(updatedValue *int, target int) bool {
	if *updatedValue > target {
		*updatedValue = target
		return true
	}
	return false
}

/*******************************************************************/

/********** bufio setting **********/

func init() {
	// bufio.ScanWords <---> bufio.ScanLines
	ReadString = newReadString(os.Stdin, bufio.ScanWords)
	stdout = bufio.NewWriter(os.Stdout)
}

/********** FAU standard libraries **********/

//fmt.Sprintf("%b\n", 255) 	// binary expression

/********** I/O usage **********/

//str := ReadString()
//i := ReadInt()
//X := ReadIntSlice(n)
//S := ReadRuneSlice()
//a := ReadFloat64()
//A := ReadFloat64Slice(n)

//str := ZeroPaddingRuneSlice(num, 32)
//str := PrintIntsLine(X...)

/*********** Input ***********/

var (
	// ReadString returns a WORD string.
	ReadString func() string
	stdout     *bufio.Writer
)

func newReadString(ior io.Reader, sf bufio.SplitFunc) func() string {
	r := bufio.NewScanner(ior)
	r.Buffer(make([]byte, 1024), int(1e+9)) // for Codeforces
	r.Split(sf)

	return func() string {
		if !r.Scan() {
			panic("Scan failed")
		}
		return r.Text()
	}
}

// ReadInt returns an integer.
func ReadInt() int {
	return int(readInt64())
}
func ReadInt2() (int, int) {
	return int(readInt64()), int(readInt64())
}
func ReadInt3() (int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64())
}
func ReadInt4() (int, int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64()), int(readInt64())
}

// ReadInt64 returns as integer as int64.
func ReadInt64() int64 {
	return readInt64()
}
func ReadInt64_2() (int64, int64) {
	return readInt64(), readInt64()
}
func ReadInt64_3() (int64, int64, int64) {
	return readInt64(), readInt64(), readInt64()
}
func ReadInt64_4() (int64, int64, int64, int64) {
	return readInt64(), readInt64(), readInt64(), readInt64()
}

func readInt64() int64 {
	i, err := strconv.ParseInt(ReadString(), 0, 64)
	if err != nil {
		panic(err.Error())
	}
	return i
}

// ReadIntSlice returns an integer slice that has n integers.
func ReadIntSlice(n int) []int {
	b := make([]int, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt()
	}
	return b
}

// ReadInt64Slice returns as int64 slice that has n integers.
func ReadInt64Slice(n int) []int64 {
	b := make([]int64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt64()
	}
	return b
}

// ReadFloat64 returns an float64.
func ReadFloat64() float64 {
	return float64(readFloat64())
}

func readFloat64() float64 {
	f, err := strconv.ParseFloat(ReadString(), 64)
	if err != nil {
		panic(err.Error())
	}
	return f
}

// ReadFloatSlice returns an float64 slice that has n float64.
func ReadFloat64Slice(n int) []float64 {
	b := make([]float64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadFloat64()
	}
	return b
}

// ReadRuneSlice returns a rune slice.
func ReadRuneSlice() []rune {
	return []rune(ReadString())
}

/*********** Output ***********/

// PrintIntsLine returns integers string delimited by a space.
func PrintIntsLine(A ...int) string {
	res := []rune{}

	for i := 0; i < len(A); i++ {
		str := strconv.Itoa(A[i])
		res = append(res, []rune(str)...)

		if i != len(A)-1 {
			res = append(res, ' ')
		}
	}

	return string(res)
}

// PrintIntsLine returns integers string delimited by a space.
func PrintInts64Line(A ...int64) string {
	res := []rune{}

	for i := 0; i < len(A); i++ {
		str := strconv.FormatInt(A[i], 10) // 64bit int version
		res = append(res, []rune(str)...)

		if i != len(A)-1 {
			res = append(res, ' ')
		}
	}

	return string(res)
}

// PrintfBufStdout is function for output strings to buffered os.Stdout.
// You may have to call stdout.Flush() finally.
func PrintfBufStdout(format string, a ...interface{}) {
	fmt.Fprintf(stdout, format, a...)
}

/*********** Debugging ***********/

// PrintfDebug is wrapper of fmt.Fprintf(os.Stderr, format, a...)
func PrintfDebug(format string, a ...interface{}) {
	fmt.Fprintf(os.Stderr, format, a...)
}

// ZeroPaddingRuneSlice returns binary expressions of integer n with zero padding.
// For debugging use.
func ZeroPaddingRuneSlice(n, digitsNum int) []rune {
	sn := fmt.Sprintf("%b", n)

	residualLength := digitsNum - len(sn)
	if residualLength <= 0 {
		return []rune(sn)
	}

	zeros := make([]rune, residualLength)
	for i := 0; i < len(zeros); i++ {
		zeros[i] = '0'
	}

	res := []rune{}
	res = append(res, zeros...)
	res = append(res, []rune(sn)...)

	return res
}