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: Time Limit Exceeded)

#define _USE_MATH_DEFINES
#define INF 0x3f3f3f3f
#include <cstdio>
#include <iostream>
#include <sstream>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <stack>
#include <limits>
#include <map>
#include <string>
#include <cstring>
#include <set>
#include <deque>
#include <bitset>
#include <list>
#include <cctype>
#include <utility>
 
using namespace std;
 
typedef long long ll;
typedef pair <int,int> P;
typedef pair <int,P > PP;
 
static const int tx[] = {0,1,0,-1};
static const int ty[] = {-1,0,1,0};
 
static const double EPS = 1e-8;

char stage[4][4];
bool has_stamp[5][5];
bool left_right_stamp[5][5];
bool top_bottom_stamp[5][5];
bool corner_stamp[5][5];

map<ll,int> dp;

ll compute_hash(){
  ll res = 0;
  for(int y = 0; y < 4; y++){
    for(int x = 0; x < 4; x++){
      ll num = 0; //stage[y][x] eq '.'
      if(stage[y][x] == 'R') num = 1;
      if(stage[y][x] == 'G') num = 2;
      if(stage[y][x] == 'B') num = 3;
      res += num;
      res <<= 2LL;
    }
  }
  return res;
}

bool is_clear(ll hash){
  return (hash == 0);
}

bool can_erase(int ly,int lx,int ry,int rx){
  int freq[256] = {};
  int colors = 0;
  for(int y = ly; y <= ry; y++){
    for(int x = lx; x <= rx; x++){
      if(stage[y][x] != '.' && 
         freq[stage[y][x]]++ == 0) colors++;
      if(colors >= 2) return false;
    }
  }
  return true;
}

void erase(int ly,int lx,int ry,int rx){
  for(int y = ly; y <= ry; y++){
    for(int x = lx; x <= rx; x++){
      stage[y][x] = '.';
    }
  }
}

void dfs(int count){
  ll hash = compute_hash();

  if(dp.find(hash) != dp.end() 
     && dp[hash] <= count) return;
  dp[hash] = count;

  for(int ly=0;ly<4;ly++){
    for(int lx=0;lx<4;lx++){
      for(int ry=ly;ry<4;ry++){
        for(int rx=lx;rx<4;rx++){
          //corner
          if((lx == 0 && ly == 0)
             || (lx == 0 && ly == 3)
             || (rx == 3 && ry == 3)
             || (rx == 3 && ry == 0)){
            if(corner_stamp[ry - ly + 1][rx - lx + 1]){
              if(can_erase(ly,lx,ry,rx)){
                char tmp[4][4];
                memcpy(tmp,stage,sizeof(char)*4*4);
                erase(ly,lx,ry,rx);
                dfs(count+1);
                memcpy(stage,tmp,sizeof(char)*4*4);
              }
            }
          }

          //left or right
          else if(lx == 0 || rx == 3){
            if(left_right_stamp[ry - ly + 1][rx - lx + 1]){
              if(can_erase(ly,lx,ry,rx)){
                  char tmp[4][4];
                  memcpy(tmp,stage,sizeof(char)*4*4);
                  erase(ly,lx,ry,rx);
                  dfs(count+1);
                  memcpy(stage,tmp,sizeof(char)*4*4);
              }
            }
          }

          //top or bottom
          else if(ly == 0 || ry == 3){
            if(top_bottom_stamp[ry - ly + 1][rx - lx + 1]){
              if(can_erase(ly,lx,ry,rx)){
                  char tmp[4][4];
                  memcpy(tmp,stage,sizeof(char)*4*4);
                  erase(ly,lx,ry,rx);
                  dfs(count+1);
                  memcpy(stage,tmp,sizeof(char)*4*4);
              }
            }
          }

          //center
          else {
            if(has_stamp[ry - ly + 1][rx - lx + 1]){
              if(can_erase(ly,lx,ry,rx)){
                  char tmp[4][4];
                  memcpy(tmp,stage,sizeof(char)*4*4);
                  erase(ly,lx,ry,rx);
                  dfs(count+1);
                  memcpy(stage,tmp,sizeof(char)*4*4);
              }
            }
          }
        }
      }
    }
  }
}

int main(){
  int num_of_stamps;
  while(~scanf("%d",&num_of_stamps)){
    memset(has_stamp,false,sizeof(has_stamp));
    memset(top_bottom_stamp,false,sizeof(top_bottom_stamp));
    memset(left_right_stamp,false,sizeof(left_right_stamp));
    memset(corner_stamp,false,sizeof(corner_stamp));
    dp.clear();

    for(int stamp_i = 0; stamp_i < num_of_stamps; stamp_i++){
      int H,W;
      scanf("%d %d",&H,&W);
      has_stamp[H][W] = true;
      for(int h = H; h >= 1; h--){
        for(int w = W; w >= 1; w--){
          corner_stamp[h][w] = true;
        }
      }
    }

    for(int h = 4; h >= 1; h--){
      bool flag = false;
      for(int w = 4; w >= 1; w--){
        if(has_stamp[h][w]) flag = true;
        if(flag) left_right_stamp[h][w] = true;
      }
    }

    for(int w = 4; w >= 1; w--){
      bool flag = false;
      for(int h = 4; h >= 1; h--){
        if(has_stamp[h][w]) flag = true;
        if(flag) top_bottom_stamp[h][w] = true;
      }
    }
    
    for(int y = 0; y < 4; y++){
      char line[8];
      scanf("%s",line);
      for(int x = 0; x < 4; x++){
        stage[y][x] = line[x];
      }
    }

    dfs(0);
    printf("%d\n",dp[0]);
  }
}