Problem:
H: Rectangular Stamps

ICPC で良い成績を収めるには修行が欠かせない．うさぎは ICPC で勝ちたいので，今日も修行をすることにした．

今日の修行は，絵を描くことによって，創造力を高めようというものである．四角いスタンプを用いて，上手く模様を描こう．

大小さまざまなスタンプを使い，4 × 4 のマス目の紙に指定された赤・緑・青の通りの絵を完成させたい．スタンプは長方形であり，マス目にぴったり合わせて使う．スタンプの縦と横を入れ替えることはできない．

紙は最初色が塗られていない状態にある．紙にスタンプを押すと，押された部分がスタンプの色に変わり，下に隠れた色は全く見えなくなる．スタンプの色は付けるインクにより決定されるので，どのスタンプでも，好きな色を選ぶことが可能である．スタンプは紙から一部がはみ出た状態で押すことも可能であり，はみ出た部分は無視される．

1 つのスタンプを複数回使うことは可能である．同じスタンプを別の色に対して使ってもよい．スタンプを押すのはやや神経を使う作業なので，出来るだけスタンプを押す回数を少なくしたい．

Input

N
H1 W1
...
HN WN
C1,1C1,2C1,3C1,4
C2,1C2,2C2,3C2,4
C3,1C3,2C3,3C3,4
C4,1C4,2C4,3C4,4

N はスタンプの個数，Hi, Wi (1 ≤ i ≤ N) はそれぞれ，i 番目のスタンプの縦の長さ，横の長さを表す整数である．Ci, j (1 ≤ i ≤ 4，1 ≤ j ≤ 4) は，上から i 行目，左から j 列目のマスについて指定された絵の色を表す文字である．赤は R ，緑は G ，青は B で表される．

1 ≤ N ≤ 16，1 ≤ Hi ≤ 4，1 ≤ Wi ≤ 4 を満たす．(Hi, Wi) として同一の組は複数回現れない．

Output

絵を完成させるためにスタンプを押さなければならない最小の回数を 1 行に出力せよ．

Sample Input 1

2
4 4
1 1
RRRR
RRGR
RBRR
RRRR

Sample Output 1

3

Sample Input 2

1
2 3
RRGG
BRGG
BRRR
BRRR

Sample Output 2

5

Solution in C++ (judge verdict: Accepted)

#include <iostream>
#include <iomanip>
#include <sstream>
#include <cstdio>
#include <string>
#include <vector>
#include <algorithm>
#include <complex>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <cassert>
#include <climits>
#include <queue>
#include <set>
#include <map>
#include <valarray>
#include <bitset>
#include <stack>
using namespace std;

#define REP(i,n) for(int i=0;i<(int)n;++i)
#define FOR(i,c) for(__typeof((c).begin())i=(c).begin();i!=(c).end();++i)
#define ALL(c) (c).begin(), (c).end()
typedef long long ll;
typedef pair<int,int> pii;
const int INF = 1<<29;
const double PI = acos(-1);
const double EPS = 1e-8;

int h[16],w[16];
int dist[1<<16];
char ba[4][4];
int ok[1<<16];

int main() {
  int n;
  cin >> n;
  REP(i,n) cin >> h[i] >> w[i];
  REP(i,4) REP(j,4) cin >> ba[i][j];
  int num = 0;
  set<int> masks;
  REP(i,n) {
    for (int y1=-h[i]+1; y1<4; ++y1) {
      for (int x1=-w[i]+1; x1<4; ++x1) {
        int mask = 0;
        for (int y=y1; y<y1+h[i]; ++y) {
          for (int x=x1; x<x1+w[i]; ++x) {
            if (y>=0&&y<4&&x>=0&&x<4) mask |= 1<<(y*4+x);
          }
        }
        masks.insert(mask);
      }
    }
  }

  // FOR(it, masks) {
  //   int S = *it;
  //   REP(y,4) {
  //     REP(x,4)
  //       cout << (S>>(y*4+x)&1);
  //     cout << endl;
  //   }
  //   cout << endl;
  // }
  REP(S,1<<16) {
    set<char> st;
    REP(y,4) {
      REP(x,4) {
        if (S>>(y*4+x)&1) {
          st.insert(ba[y][x]);
        }
      }
    }
    ok[S] = (st.size() <= 1);
    // if (ok[S]) {
    //   REP(y,4) {
    //     REP(x,4)
    //       cout << (S>>(y*4+x)&1);
    //     cout << endl;
    //   }
    //   cout << endl;
    // }
  }
  queue<int> Q;
  Q.push((1<<16)-1);
  memset(dist,-1,sizeof(dist));
  dist[(1<<16)-1] = 0;
  while(!Q.empty()) {
    int S = Q.front(); Q.pop();
    if (S == 0) break;
    FOR(it, masks) {
      int t[3]={};
      if (ok[*it & S]) {
        int T = S & ~*it;
        if (dist[T] == -1) {
          dist[T] = dist[S] + 1;
          Q.push(T);
        }
      }
    }
  }
  cout << dist[0] << endl;
}