/*
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[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
}

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
}