Problem:
Score : 500 points

Problem Statement

We have a chocolate bar partitioned into H horizontal rows and W vertical columns of squares.

The square (i, j) at the i-th row from the top and the j-th column from the left is dark if S_{i,j} is 0, and white if S_{i,j} is 1.

We will cut the bar some number of times to divide it into some number of blocks. In each cut, we cut the whole bar by a line running along some boundaries of squares from end to end of the bar.

How many times do we need to cut the bar so that every block after the cuts has K or less white squares?

Constraints

1 \leq H \leq 10

1 \leq W \leq 1000

1 \leq K \leq H \times W

S_{i,j} is 0 or 1.

Input

Input is given from Standard Input in the following format:

H W K
S_{1,1}S_{1,2}...S_{1,W}
:
S_{H,1}S_{H,2}...S_{H,W}

Output

Print the number of minimum times the bar needs to be cut so that every block after the cuts has K or less white squares.

Sample Input 1

3 5 4
11100
10001
00111

Sample Output 1

2

For example, cutting between the 1-st and 2-nd rows and between the 3-rd and 4-th columns - as shown in the figure to the left - works.

Note that we cannot cut the bar in the ways shown in the two figures to the right.

Sample Input 2

3 5 8
11100
10001
00111

Sample Output 2

0

No cut is needed.

Sample Input 3

4 10 4
1110010010
1000101110
0011101001
1101000111

Sample Output 3

3

Solution in C# (Mono 4.6.2.0) (judge verdict: Time Limit Exceeded)

using System;
using System.Numerics;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Collections;

namespace debug
{
    class main
    {
        static void Main(string[] args)
        {
            //問題クラスを展開
            ProgramE a = new ProgramE();
            a.main();//実行する

        }

    }

    class ProgramA
    {
        public void main()
        {
            //入力
            string[] input = Console.ReadLine().Split(' ');
            int m = int.Parse(input[0]);
            int n = int.Parse(input[1]);

            //偶数同士の組み合わせと奇数同士の組み合わせなので、mC2とnC2を求める
            Console.WriteLine((m - 1) * m / 2 + n * (n - 1) / 2);
        }
    }

    class ProgramB
    {
        public void main()
        {

            //入力
            string s = Console.ReadLine();
            int len = s.Length;

            for (int i = 0; i < (len - 1) / 2; i++)
            {
                //回文かみる
                if (s[i] != s[(len - 1) - i])
                {
                    Console.WriteLine("No");
                    return;
                }

                //前半が回文かみる
                if (s[i] != s[(len - 1) / 2 - i])
                {
                    Console.WriteLine("No");
                    return;
                }

                //後半が回文かみる
                if (s[(len + 3) / 2 + i] != s[len - 1 - i])
                {
                    Console.WriteLine("No");
                    return;
                }

            }

            Console.WriteLine("Yes");

        }
    }

    class ProgramC
    {
        public void main()
        {

            //入力
            double l = double.Parse(Console.ReadLine());

            //a=b=c=l/3のとき最大
            Console.WriteLine((l / 3.0) * (l / 3.0) * (l / 3.0));
        }
    }

    class ProgramD
    {
        public void main()
        {

            //入力
            long n = long.Parse(Console.ReadLine());
            string[] a = Console.ReadLine().Split(' ');
            long[] num = new long[n];
            long sum = 0;

            //それぞれ数がいくつあるか数える
            for (int i = 0; i < n; i++)
            {
                long temp = long.Parse(a[i]);
                num[temp - 1]++;
            }

            //とりあえずすべての数え方を見る
            for (int i = 0; i < n; i++)
            {
                sum += num[i] * (num[i] - 1) / 2;
            }

            //1つずつ禁止されたときに場合を除く（n-1する）。答を出力
            for (int i = 0; i < n; i++)
            {
                long temp = long.Parse(a[i]);
                Console.WriteLine(sum - num[temp - 1] + 1);
            }

        }


    }




    class ProgramE
    {
        public void main()
        {

            //入力
            string[] input = Console.ReadLine().Split(' ');
            int h = int.Parse(input[0]);
            int w = int.Parse(input[1]);
            int k = int.Parse(input[2]);


            //チョコの場所と白の位置把握
            int[,] choco = new int[h, w];
            int count = 0;

            for (int i = 0; i < h; i++)
            {
                string input2 = Console.ReadLine();
                for (int j = 0; j < w; j++)
                {
                    choco[i, j] = input2[j] - '0';
                    if (choco[i, j] == 1)
                        count++;
                }

            }

            //白がk個以下なら切る必要はない
            if (count <= k)
            {
                Console.WriteLine("0");
                return;
            }

            //切る回数
            int ans = 1000000;
            int sum = 0;
            //切った場所の記録
            int[] div = new int[h];
            int[] comb = new int[h];
            int part = 0;
            int flag = 0;

            //縦の切り口を決める
            for (int i = 0; i < (1 << (h - 1)); i++)
            {
                sum = 0;
                //まずは横の切り口を記録
                for (int j = 0; j < h - 1; j++)
                {
                    div[j] = 0;//毎回リセット
                    comb[j] = 0;
                    if (((i >> j) & 1) == 1)
                    {
                        sum++;//切る数を足す
                        div[j] = 1;//切ったら記憶
                    }
                }

                //貪欲法で縦の切る回数を数える
                for (int j = 0; j < w; j++)
                {
                    part = 0;
                    //それぞれの部分で分けながらカウントしていく
                    for (int l = 0; l < h; l++)
                    {
                        comb[part] += choco[l, j];

                        if (comb[part] > k)
                        {
                            flag = 1;
                            break;
                        }

                        part += div[l];

                    }

                    //もしk超えてしまったら出す
                    if (flag == 1)
                    {
                        //リセット
                        for (int l = 0; l < h; l++)
                            comb[l] = 0;

                        sum++;//切り方として足す
                        flag = 0;
                        j--;//一つ前からやり直し
                        if (ans <= sum)
                            break;
                    }
                }

                //最小更新
                if (ans > sum)
                    ans = sum;

            }


            Console.WriteLine(ans);

        }
    }


    class ProgramF
    {
        public void main()
        {

            string[] s = Console.ReadLine().Split(' ');
            int n = int.Parse(s[0]);
            int num = int.Parse(s[1]);
            long MOD = 998244353;

            string[] a = Console.ReadLine().Split(' ');
            long[,] dp = new long[n + 1,num + 1];
            //初項設定
            dp[0, 0] = 1;

            //i番目までで、選び方によって出てくる和（j）を求める
            for (int i = 0;i < n;i++)
                for(int j = 0;j < num + 1;j++)
                {
                    long temp = j + long.Parse(a[i]);

                    //3001以上は答にならないので省く
                    //入れる場合は、Tとして選んでかつその和に組み込むのでそのまま足す。
                    if (temp <= num)
                    {
                        dp[i + 1, temp] += dp[i, j];
                        dp[i + 1, temp] %= MOD;

                    }
                    //いれない場合は集合として成立しているのでそのi番目を選ぼうが選ばなかろうが部分として考えなければよい。よって、2倍して拡張
                    dp[i + 1, j] += (dp[i, j] * 2) %MOD;
                    dp[i + 1, j] %= MOD;
                }

            //答はdp[n,num]で出てくる
            Console.WriteLine(dp[n, num]);

        }
    }
}