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 Rust (1.42.0) (judge verdict: Accepted)

use std::io::Read;
use std::cmp::max;

#[allow(non_snake_case)]
fn main() {
  let mut buf = String::new();
  std::io::stdin().read_to_string(&mut buf).unwrap();
  let mut S : Vec<usize> = buf.split_whitespace().map(|s| s.parse().unwrap()).collect();
  let N = S[0];

  let mut dp1 = vec![None; N + 1];
  let mut dp2 = vec![vec![None; N + 1];N + 1];
  {
    let s = &mut S[1 ..= 2];
    s.sort();
    let (a,b) = (s[0], s[1]);
    dp1[a].get_or_insert(0);
    dp1[b].get_or_insert(0);
    dp1[0].get_or_insert(0);
    dp2[a][b].get_or_insert(0);
  }

  let mut base = 0;
  let mut q = Vec::new();

  for i in 1 .. N {
    let s = &mut S[3 * i .. 3 * (i + 1)];
    s.sort();
    let (a,b,c) = (s[0],s[1],s[2]);
    
    if a == c {
      base += 1;
      continue;
    } else if a == b {
      for x in 1 ..= N {
        if x < a {
          if let Some(v) = dp2[x][a] {
            q.push((x, c, v + 1));
          }
        } else if x < c {
          if let Some(v) = dp2[a][x] {
            q.push((x, c, v + 1));
          }
        } else {
          if let Some(v) = dp2[a][x] {
            q.push((c, x, v + 1));
          }
        }
      }
      if let Some(v) = dp2[c][c] {
        q.push((a, a, v + 1));
      }
    } else if b == c {
      for x in 1 ..= N {
        if x < a {
          if let Some(v) = dp2[x][c] {
            q.push((x, a, v + 1));
          }
        } else if x < c {
          if let Some(v) = dp2[x][c] {
            q.push((a, x, v + 1));
          }
        } else {
          if let Some(v) = dp2[c][x] {
            q.push((a, x, v + 1));
          }
        }
      }
      if let Some(v) = dp2[a][a] {
        q.push((c, c, v + 1));
      }
    } else {
      if let Some(v) = dp2[a][a] {
        q.push((b, c, v + 1));
      }
      if let Some(v) = dp2[b][b] {
        q.push((a, c, v + 1));
      }
      if let Some(v) = dp2[c][c] {
        q.push((a, b, v + 1));
      }
    }
    
    q.push((a, b, dp1[0].unwrap()));
    q.push((a, c, dp1[0].unwrap()));
    q.push((b, c, dp1[0].unwrap()));
    
    for x in 1 ..= N {
      if let Some(v) = dp1[x] {
        for y in &[a,b,c] {
          if x < *y {
            q.push((x, *y, v));
          } else {
            q.push((*y, x, v));
          }
        }
      }
    }
    
    while let Some((a,b,v)) = q.pop() {
      if dp2[a][b].is_none() || dp2[a][b].unwrap() < v {
        let u = dp2[a][b].replace(v);
        for i in &[0, a, b] {
          let u = dp1[*i].get_or_insert(v);
          *u = max(*u, v);
        }
      }
    }
  }

  let s = S[S.len() - 1];
  println!("{}", match dp2[s][s] {
    Some(v) => base + max(v + 1, dp1[0].unwrap()),
    None    => base + dp1[0].unwrap(), 
  });
}