Problem:
Score : 600 points

Problem Statement

We have 3N cards arranged in a row from left to right, where each card has an integer between 1 and N (inclusive) written on it. The integer written on the i-th card from the left is A_i.

You will do the following operation N-1 times:

Rearrange the five leftmost cards in any order you like, then remove the three leftmost cards. If the integers written on those three cards are all equal, you gain 1 point.

After these N-1 operations, if the integers written on the remaining three cards are all equal, you will gain 1 additional point.

Find the maximum number of points you can gain.

Constraints

1 \leq N \leq 2000

1 \leq A_i \leq N

Input

Input is given from Standard Input in the following format:

N
A_1 A_2 \cdots A_{3N}

Output

Print the maximum number of points you can gain.

Sample Input 1

2
1 2 1 2 2 1

Sample Output 1

2

Let us rearrange the five leftmost cards so that the integers written on the six cards will be 2\ 2\ 2\ 1\ 1\ 1 from left to right.

Then, remove the three leftmost cards, all of which have the same integer 2, gaining 1 point.

Now, the integers written on the remaining cards are 1\ 1\ 1.

Since these three cards have the same integer 1, we gain 1 more point.

In this way, we can gain 2 points - which is the maximum possible.

Sample Input 2

3
1 1 2 2 3 3 3 2 1

Sample Output 2

1

Sample Input 3

3
1 1 2 2 2 3 3 3 1

Sample Output 3

3

Solution in C# (.NET Core 3.1.201) (judge verdict: Runtime Error)

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Pair = System.Tuple<int,int>;


class TEST{
	static void Main(){
		Sol mySol =new Sol();
		mySol.Solve();
	}
}

class Sol{
	const int M = 4096;
	int enc(int x, int y){
		if(x > y) return enc(y, x);
		return y * M + x;
	}
	int score(int x, int y, int z){ return (x == y && y == z) ? 1 : 0;}
	public void Solve(){
		
		var di = new Dictionary<int, int>();
		int a,b,c,d,e;
		int h;
		
		a = A[0]; b = A[1]; c = A[2]; d = A[3]; e = A[4];
		if(!di.ContainsKey(enc(a, b))) di.Add(enc(a, b),0); h = enc(a, b); di[h] = Math.Max(di[h], score(c, d, e));
		if(!di.ContainsKey(enc(a, c))) di.Add(enc(a, c),0); h = enc(a, c); di[h] = Math.Max(di[h], score(b, d, e));
		if(!di.ContainsKey(enc(a, d))) di.Add(enc(a, d),0); h = enc(a, d); di[h] = Math.Max(di[h], score(b, c, e));
		if(!di.ContainsKey(enc(a, e))) di.Add(enc(a, e),0); h = enc(a, e); di[h] = Math.Max(di[h], score(b, c, d));
		if(!di.ContainsKey(enc(b, c))) di.Add(enc(b, c),0); h = enc(b, c); di[h] = Math.Max(di[h], score(a, d, e));
		if(!di.ContainsKey(enc(b, d))) di.Add(enc(b, d),0); h = enc(b, d); di[h] = Math.Max(di[h], score(a, c, e));
		if(!di.ContainsKey(enc(b, e))) di.Add(enc(b, e),0); h = enc(b, e); di[h] = Math.Max(di[h], score(a, c, d));
		if(!di.ContainsKey(enc(c, d))) di.Add(enc(c, d),0); h = enc(c, d); di[h] = Math.Max(di[h], score(a, b, e));
		if(!di.ContainsKey(enc(c, e))) di.Add(enc(c, e),0); h = enc(c, e); di[h] = Math.Max(di[h], score(a, b, d));
		if(!di.ContainsKey(enc(d, e))) di.Add(enc(d, e),0); h = enc(d, e); di[h] = Math.Max(di[h], score(a, b, c));
		
		for(int i=0;i<N-2;i++){
			var ndi = new Dictionary<int, int>();
			foreach(var h0 in di.Keys){
				a = h0 / M; b = h0 % M; c = A[5 + i]; d = A[6 + i]; e = A[7 + i];
				int sc = di[h0];
				if(!ndi.ContainsKey(enc(a, b))) ndi.Add(enc(a, b),0); h = enc(a, b); ndi[h] = Math.Max(ndi[h], sc + score(c, d, e));
				if(!ndi.ContainsKey(enc(a, c))) ndi.Add(enc(a, c),0); h = enc(a, c); ndi[h] = Math.Max(ndi[h], sc + score(b, d, e));
				if(!ndi.ContainsKey(enc(a, d))) ndi.Add(enc(a, d),0); h = enc(a, d); ndi[h] = Math.Max(ndi[h], sc + score(b, c, e));
				if(!ndi.ContainsKey(enc(a, e))) ndi.Add(enc(a, e),0); h = enc(a, e); ndi[h] = Math.Max(ndi[h], sc + score(b, c, d));
				if(!ndi.ContainsKey(enc(b, c))) ndi.Add(enc(b, c),0); h = enc(b, c); ndi[h] = Math.Max(ndi[h], sc + score(a, d, e));
				if(!ndi.ContainsKey(enc(b, d))) ndi.Add(enc(b, d),0); h = enc(b, d); ndi[h] = Math.Max(ndi[h], sc + score(a, c, e));
				if(!ndi.ContainsKey(enc(b, e))) ndi.Add(enc(b, e),0); h = enc(b, e); ndi[h] = Math.Max(ndi[h], sc + score(a, c, d));
				if(!ndi.ContainsKey(enc(c, d))) ndi.Add(enc(c, d),0); h = enc(c, d); ndi[h] = Math.Max(ndi[h], sc + score(a, b, e));
				if(!ndi.ContainsKey(enc(c, e))) ndi.Add(enc(c, e),0); h = enc(c, e); ndi[h] = Math.Max(ndi[h], sc + score(a, b, d));
				if(!ndi.ContainsKey(enc(d, e))) ndi.Add(enc(d, e),0); h = enc(d, e); ndi[h] = Math.Max(ndi[h], sc + score(a, b, c));
			
			}
			di = ndi;
		}
		
		int ma = 0;
		foreach(var h0 in di.Keys){
			a = h0 / M; b = h0 % M; c = A[A.Length-1];
			int sc = di[h0] + score(a, b, c);
			ma = Math.Max(ma, sc);
		}
		
		Console.WriteLine(ma);
		
	}
	int N;
	int[] A;
	public Sol(){
		N = ri();
		A = ria();
	}

	static String rs(){return Console.ReadLine();}
	static int ri(){return int.Parse(Console.ReadLine());}
	static long rl(){return long.Parse(Console.ReadLine());}
	static double rd(){return double.Parse(Console.ReadLine());}
	static String[] rsa(char sep=' '){return Console.ReadLine().Split(sep);}
	static int[] ria(char sep=' '){return Array.ConvertAll(Console.ReadLine().Split(sep),e=>int.Parse(e));}
	static long[] rla(char sep=' '){return Array.ConvertAll(Console.ReadLine().Split(sep),e=>long.Parse(e));}
	static double[] rda(char sep=' '){return Array.ConvertAll(Console.ReadLine().Split(sep),e=>double.Parse(e));}
}