Problem:
ビートパネル

遊太君は、近所のゲームセンターで人気のゲーム「ビートパネル」にはまっています。このゲームは図のようなグリッド状に並べられた 4×4 の計16個のパネル型のボタンからなります。

図のように、左上から右下にボタン1, ボタン2, …, ボタン16の順にボタンが並んでいます。ゲームでは一定間隔でビート音が鳴り最後に終了音が鳴ります。ビート音が鳴ると同時に複数のボタンが光ります。１個も光らない場合もあります。プレイヤーは、ビート音が鳴った直後から次の音が鳴るまでの間に両手の指を使って複数のボタンを１度だけ同時に押すことができます。何も押さないということも可能です。終了音が鳴ったと同時にゲームは終了します。

遊太君は c 通りの押し方を習得しており、ビート音が鳴る度に、それらの押し方の中から１つ決めてボタンを押します。押したボタンが光っていれば、それらのボタンの光は消え、消えたボタンの数がプレイヤーのスコアに加算されます。また、一度光ったボタンはそのボタンが押されるまで光は消えることはありません。

ビート音が n 回鳴るときのボタンの光り方と遊太君が習得している c 通りのボタンの押し方を入力とし、遊太君が獲得することのできる得点の最大値を出力するプログラムを作成してください。

入力

入力は複数のデータセットからなる。入力の終わりはゼロ２つの行で示される。各データセットは以下の形式で与えられる。

n c
a1,1 a1,2 ... a1,16
a2,1 a2,2 ... a2,16
...
an,1 an,2 ... an,16
b1,1 b1,2 ... b1,16
b2,1 b2,2 ... b2,16
...
bc,1 bc,2 ... bc,16

１行目は１つの空白で区切られた２つの整数からなる。n (1 ≤ n ≤ 30) はビート音の数、c (1 ≤ c ≤ 30) は習得しているボタンの押し方の数を示す。続く n＋c 行にボタンの光り方とボタンの押し方が与えられる。行中の入力項目の区切りは１つの空白である。ak,i は k 回目のビート音が鳴ったときのボタン i の光り方を、bj,i は c 通り中 j 番目の押し方でボタン i が押されるかどうかを表す。ak,iの値は 0 が「光らない」、1 が「光る」を表す。bj,i の値は 0 が「押さない」、1 が「押す」を表す。

データセットの数は 20 を超えない。

出力

各データセットごとに、得点の最大値を１行に出力する。

入力例

2 2
0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1
1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0
0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1
1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0
2 2
0 0 1 0 1 1 1 1 0 0 1 0 0 0 1 0
0 1 0 1 0 1 1 1 0 1 0 1 0 0 0 0
0 0 1 0 1 0 0 0 0 0 1 0 0 0 1 0
0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0
5 3
0 0 0 0 0 1 1 0 0 1 1 0 0 0 0 0
1 0 0 1 0 1 1 0 0 1 1 0 1 0 0 1
0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0
0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0
0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 0
0 0 0 0 0 1 1 0 0 1 1 0 0 0 0 0
1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0
0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1
0 0

出力例

8
7
16

Solution in C# (judge verdict: Accepted)

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

namespace _0263
{
    class Program
    {
        const int BITMAX = 1 << 16;
        static int n, c;
        static int[] ptn;
        static int[] push;

        static void Main(string[] args)
        {
            while (true)
            {
                int[] nc = RArInt();
                if (nc.Sum() == 0) break;
                Init(nc);
                Console.WriteLine(GetMaxScore());
            }

        }

        private static void Init(int[] nc)
        {
            n = nc[0]; c = nc[1];
            ptn = new int[n];
            for (int i = 0; i < ptn.Length; i++)
            {
                int[] vs = RArInt();
                for (int j = 0; j < vs.Length; j++)
                {
                    ptn[i] = (ptn[i] << 1) + vs[j];
                }
            }

            push = new int[c];
            for (int i = 0; i < push.Length; i++)
            {
                int[] vs = RArInt();
                for (int j = 0; j < vs.Length; j++)
                {
                    push[i] = (push[i] << 1) + vs[j];
                }
            }
        }

        private static int GetMaxScore()
        {
            int[,] score = new int[n + 1, BITMAX];
            for (int i = 0; i < n + 1; i++)
            {
                for (int j = 0; j < BITMAX; j++)
                {
                    score[i, j] = -1;
                }
            }
            score[0, 0] = 0;

            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < BITMAX; j++)
                {
                    if (score[i, j] > -1)
                    {
                        int board = j | ptn[i];
                        for (int k = 0; k < push.Count(); k++)
                        {
                            int point = BitCount(board & push[k]);
                            int next = board & ~push[k];
                            score[i + 1, next] = Math.Max(score[i + 1, next], score[i, j] + point);
                        }
                    }
                }
            }

            int res = 0;
            for (int i = 0; i < BITMAX; i++) res = Math.Max(res, score[n, i]);

            return res;
        }

        /// <summary>
        /// ビットの立っている個数をカウントする
        /// </summary>
        /// <param name="bits">int型整数</param>
        /// <returns></returns>
        private static int BitCount(int bits)
        {
            bits = (bits & 0x55555555) + (bits >> 1 & 0x55555555);    //  2bitごとに計算
            bits = (bits & 0x33333333) + (bits >> 2 & 0x33333333);    //  4bitごとに計算
            bits = (bits & 0x0f0f0f0f) + (bits >> 4 & 0x0f0f0f0f);    //  8bitごとに計算
            bits = (bits & 0x00ff00ff) + (bits >> 8 & 0x00ff00ff);    //  16ビットごとに計算   
            return (bits & 0x0000ffff) + (bits >> 16);
        }

        static string RSt() { return Console.ReadLine(); }
        static int RInt() { return int.Parse(Console.ReadLine().Trim()); }
        static long RLong() { return long.Parse(Console.ReadLine().Trim()); }
        static double RDouble() { return double.Parse(Console.ReadLine()); }
        static string[] RArSt(char sep = ' ') { return Console.ReadLine().Trim().Split(sep); }
        static int[] RArInt(char sep = ' ') { return Array.ConvertAll(Console.ReadLine().Trim().Split(sep), e => int.Parse(e)); }
        static long[] RArLong(char sep = ' ') { return Array.ConvertAll(Console.ReadLine().Trim().Split(sep), e => long.Parse(e)); }
        static double[] RArDouble(char sep = ' ') { return Array.ConvertAll(Console.ReadLine().Trim().Split(sep), e => double.Parse(e)); }
        static string WAr<T>(IEnumerable<T> array, string sep = " ") { return string.Join(sep, array.Select(x => x.ToString()).ToArray()); }
    }
}