// Solution for problem Rummikub,  BAPC 2006
// Uses dynamic programming
// Includes some checks on correctness input
//
// Rudy van Vliet, 10 August 2006

#include <iostream>
using namespace std;

const int MaxN = 100;
const int MaxMaxGap = 4;  // 3 would probably be sufficient

bool Cards[MaxN+1][5];    // (1..MaxN)x(1..4) are used
int StreetValues[MaxN+2][MaxN+2][5]; 
                          // (0..MaxN+1)x(0..MaxN+1)x(1..4) are used
bool Bridges[MaxN+1][5];  // (1..MaxN)x(0..4) are used
int Best[MaxN+1][5][MaxMaxGap+1];  
                          // (0..MaxN+1)x(0..4)x(0..MaxMaxGap) are used

//***************************************************************************

bool ReadCards (int NrCards)
 // read in NrCards cards 
 // check if the cards have correct numbers and colours and
 // if no cards are occurring more than once
{ int h, c,
      i;
  bool OK;
  char ch;

  for (h=1;h<=MaxN;h++)
    for (c=1;c<=4;c++)
      Cards[h][c] = false;

  OK = true;
  for (i=1;(i<=NrCards) && OK;i++)
  { cin >> h >> ch;

    if ((h<1) || (h>MaxN))
    { cout << "Wrong number of card " << h << ch << '\n';
      OK = false;
    }
    switch (ch)
    { case 'r': c=1;
                break;
      case 'g': c=2;
                break;
      case 'b': c=3;
                break;
      case 'y': c=4;
                break;
      default: cout << "Wrong colour of card " << h << ch << '\n';
               OK = false;
    }

    if (OK)
      if (Cards[h][c])
      { cout << "Double card " << h << ch << '\n';
        OK = false;
      }
      else
        Cards[h][c] = true;

  }  // for

  return OK;

}  // ReadCards

//***************************************************************************

void ComputeStreetValues ()
 // compute values of all possible `street sequences' of cards
 // (cards with the same colour, and a certain minimal and maximal value)
{ int h0, h1,
      c,
      Length,
      LengthLastStreet[MaxN+2][MaxN+2][5]; 
        // (0..MaxN+1)x(0..MaN+1)x(1..4) are used

  for (h0=0;h0<=MaxN+1;h0++)
    for (h1=0;h1<=MaxN+1;h1++)
      for (c=1;c<=4;c++)
      { StreetValues[h0][h1][c] = 0;
        LengthLastStreet[h0][h1][c] = 0;
      }

  for (h0=1;h0<=MaxN;h0++)
    for (h1=h0;h1<=MaxN;h1++)
      for (c=1;c<=4;c++)
      { if (h1==h0)
          Length = 0;
        else
          Length = LengthLastStreet[h0][h1-1][c];

        if (Cards[h1][c])
        { Length ++;
          if (Length<=2)
             // note: h1>=h0>=1, so h1-1>=0
            StreetValues[h0][h1][c] = StreetValues[h0][h1-1][c];
          else
            if (Length==3)
               // in particular: h1-2>=h0>=1, so h1-3>=0
              StreetValues[h0][h1][c] = (h1-2) + (h1-1) + h1 +
                StreetValues[h0][h1-3][c];
            else  // Length>=4
                  // in particular: h1-1>=h0
              StreetValues[h0][h1][c] = h1 + StreetValues[h0][h1-1][c];
        }
        else  // Cards is not available
        { Length = 0;
          StreetValues[h0][h1][c] = StreetValues[h0][h1-1][c];
        }

        LengthLastStreet[h0][h1][c] = Length;

      }  // for c
        
}  // ComputeStreetValues

//***************************************************************************
        
void DetermineBridges ()
 // determine the possible `bridges' in the cardset
{ int h, c,
      NumberColoursMissing,
      LastColourMissing;

  for (h=1;h<=MaxN;h++)
    for (c=0;c<=4;c++)
      Bridges[h][c] = false;

  for (h=1;h<=MaxN;h++)
  { NumberColoursMissing = 0;
    LastColourMissing = 0;

    for (c=1;c<=4;c++)
      if (!(Cards[h][c]))
      { NumberColoursMissing ++;
        LastColourMissing = c;
      }

    if (NumberColoursMissing==0)  // all colours available
      for (c=0;c<=4;c++)
        Bridges[h][c] = true;
    else
      if (NumberColoursMissing==1)  // one colour missing
           // one bridge of length 3 is possible
        Bridges[h][LastColourMissing] = true;
  }  // for h

}  // DetermineBridges

//***************************************************************************

int Minim (int x, int y)
 // return the minimum of x and y
{
  if (x<=y)
    return x;
  else
    return y;

}  // Minim

//***************************************************************************

void ComputeBest ()
 // Compute all entries of Best: the highest possible values with
 // cardnumbers up to `h0', a gap at colour `c0' and gap size `gap0'.
{ int h0, h,
      LastBridge,
      c0, c1, c,
      mingap0, maxgap0, gap0,
      mingap1, maxgap1, gap1,
      ValueBridge, ValueAbove, Value;

   // initialize Best with only StreetValues
  for (c0=0;c0<=4;c0++)
    Best[0][c0][0] = 0;
  for (h0=1;h0<=MaxN;h0++)
  { for (c0=0;c0<=4;c0++)
    { if (c0==0)
        Best[h0][c0][0] = StreetValues[1][h0][1] + StreetValues[1][h0][2] +
          StreetValues[1][h0][3] + StreetValues[1][h0][4];
      else
      { mingap0 = 1;
        maxgap0 = Minim (h0, MaxMaxGap);
        for (gap0=mingap0;gap0<=maxgap0;gap0++)
          Best[h0][c0][gap0] = Best[h0][0][0] - StreetValues[1][h0][c0] +
            StreetValues[1][h0-gap0][c0];
      }
    }  // for c0
  }  // for h0

    // search for better values with bridges in between
  for (h0=1;h0<=MaxN;h0++)
    for (c0=0;c0<=4;c0++)
    { if (c0==0)
      { mingap0 = 0;
        maxgap0 = 0;
      }
      else
      { mingap0 = 1;
        maxgap0 = Minim (h0, MaxMaxGap);
      }
      for (gap0=mingap0;gap0<=maxgap0;gap0++)
      {  
         // the last bridge used can be below the gap or
         // next to the gap;
         // first we consider cases where the last bridge is below
         // the gap
        for (LastBridge=1;LastBridge<=h0-gap0;LastBridge++)
        { // we may have a bridge of size 4 or any bridge of size 3 
          for (c1=0;c1<=4;c1++)
            if (Bridges[LastBridge][c1])
              // this bridge exists!
            {  // compute value of the bridge itself and the streets
               // above it
              if (c1==0)
                ValueBridge = 4*LastBridge;
              else
                ValueBridge = 3*LastBridge;
              ValueAbove = 0;
              for (c=1;c<=4;c++)
                if (c!=c1)
                  // this colour is part of the bridge
                  // add the StreetValues above it
                { if (c==c0)
                    h = h0-gap0;
                  else
                    h = h0;
                  ValueAbove += StreetValues[LastBridge+1][h][c];
                    // no problem if LastBridge+1 > h
                }

              if (c1==0)  // bridge of size 4, which clearly divides
                   // the remaining cards in two sections: above and below
              { Value = ValueBridge + ValueAbove + Best[LastBridge-1][0][0];
                   // no problem if LastBridge=1
                if (Value > Best[h0][c0][gap0])
                  Best[h0][c0][gap0] = Value;
              }
              else
                // c1>=1  // bridge of size 3
                // consider all possible streets passing this bridge
              { if (c1==c0)
                  h = h0-gap0;
                else
                  h = h0;
              
                  // first consider a street not coming below the bridge
                  // (possibly next to the bridge)
                Value = ValueBridge + ValueAbove + 
                          StreetValues[LastBridge][h][c1] +
                          Best[LastBridge-1][0][0];
                  // no problem if LastBridge==1
                if (Value > Best[h0][c0][gap0])
                  Best[h0][c0][gap0] = Value;
               
                  // now consider steets sticking below the bridge
                  // they leave a gap at level LastBridge-1
                mingap1 = 1;
                maxgap1 = Minim (LastBridge-1,MaxMaxGap);
                for (gap1=mingap1;gap1<=maxgap1;gap1++)
                { Value = ValueBridge + ValueAbove +
                            StreetValues[LastBridge-gap1][h][c1] +
                            Best[LastBridge-1][c1][gap1];
                  if (Value > Best[h0][c0][gap0])
                    Best[h0][c0][gap0] = Value;
                }  // for gap1
                
              }  // bridge of size 3
            }  // bridge exists
        }  // for LastBridge

         // now we consider cases where the last bridge is next to
         // the gap
        for (LastBridge=h0-gap0+1;LastBridge<=h0;LastBridge++)
        {  // c0 >= 1, because if c0==0, then gap0=0
           // we may only have a bridge of size 3 with colour c0 missing
          if (Bridges[LastBridge][c0])
            // this bridge of size 3 exists!
          { gap1 = gap0 - (h0 - (LastBridge-1));  // remaining gap
            if (gap1==0)  // no gap remaining below the bridge
              c1 = 0;
            else  // a (smaller) gap remaining below the bridge
              c1 = c0;
            ValueBridge = 3 * LastBridge;
            ValueAbove = 0;
            for (c=1;c<=4;c++)
              if (c!=c0)
                ValueAbove +=StreetValues[LastBridge+1][h0][c];
                  // No problem if (LastBridge+1>h0).
                  // If (MaxMaxGap<=3) then (LastBridge>=h0-2)
                  // and (LastBridge+1>=h0-1); hence, in that case
                  // ValueAbove remains 0.

            Value = ValueBridge + ValueAbove +
                      Best[LastBridge-1][c1][gap1];  // value below bridge
            if (Value > Best[h0][c0][gap0])
              Best[h0][c0][gap0] = Value;
            
          }  // Bridge exists
        }  // for LastBridge

      }  // for gap0
    }  // for c0

}  // ComputeBest

//***************************************************************************

int main ()
{ int NrInst, i,
      NrCards;

  cin >> NrInst;
  for (i=1;i<=NrInst;i++)
  { cin >> NrCards;
    if (ReadCards (NrCards))
    { ComputeStreetValues ();
      DetermineBridges ();
      ComputeBest ();
      cout << Best[MaxN][0][0] << '\n';
    }
  }  // for i

  return 0;

}

