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++ (GCC 9.2.1) (judge verdict: Wrong Answer)

/* #region header */
#ifdef LOCAL
#include "/Users/takakurashokichi/Desktop/atcoder/cxx-prettyprint-master/prettyprint.hpp"
#define debug(x) cout << x << endl
#else
#define debug(...) 42
#endif

#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
// types
using namespace std;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
typedef pair<ll, ll> Pl;
typedef pair<int, int> Pi;
typedef vector<ll> vl;
typedef vector<int> vi;
typedef vector<char> vc;
template <typename T>
using mat = vector<vector<T>>;
typedef vector<vector<int>> vvi;
typedef vector<vector<long long>> vvl;
typedef vector<vector<char>> vvc;

template <int mod>
struct modint {
    int x;

    modint() : x(0) {}

    modint(int64_t y) : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}

    modint &operator+=(const modint &p) {
        if ((x += p.x) >= mod) x -= mod;
        return *this;
    }

    modint &operator-=(const modint &p) {
        if ((x += mod - p.x) >= mod) x -= mod;
        return *this;
    }

    modint &operator*=(const modint &p) {
        x = (int)(1LL * x * p.x % mod);
        return *this;
    }

    modint &operator/=(const modint &p) {
        *this *= p.inverse();
        return *this;
    }

    modint operator-() const { return modint(-x); }

    modint operator+(const modint &p) const { return modint(*this) += p; }

    modint operator-(const modint &p) const { return modint(*this) -= p; }

    modint operator*(const modint &p) const { return modint(*this) *= p; }

    modint operator/(const modint &p) const { return modint(*this) /= p; }

    bool operator==(const modint &p) const { return x == p.x; }

    bool operator!=(const modint &p) const { return x != p.x; }

    modint inverse() const {
        int a = x, b = mod, u = 1, v = 0, t;
        while (b > 0) {
            t = a / b;
            swap(a -= t * b, b);
            swap(u -= t * v, v);
        }
        return modint(u);
    }

    modint pow(int64_t n) const {
        modint ret(1), mul(x);
        while (n > 0) {
            if (n & 1) ret *= mul;
            mul *= mul;
            n >>= 1;
        }
        return ret;
    }

    friend ostream &operator<<(ostream &os, const modint &p) {
        return os << p.x;
    }

    friend istream &operator>>(istream &is, modint &a) {
        int64_t t;
        is >> t;
        a = modint<mod>(t);
        return (is);
    }

    static int get_mod() { return mod; }
};
// abreviations
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
#define rep_(i, a_, b_, a, b, ...) for (ll i = (a), max_i = (b); i < max_i; i++)
#define rep(i, ...) rep_(i, __VA_ARGS__, __VA_ARGS__, 0, __VA_ARGS__)
#define rrep_(i, a_, b_, a, b, ...) \
    for (ll i = (b - 1), min_i = (a); i >= min_i; i--)
#define rrep(i, ...) rrep_(i, __VA_ARGS__, __VA_ARGS__, 0, __VA_ARGS__)
#define SZ(x) ((int)(x).size())
#define pb(x) push_back(x)
#define eb(x) emplace_back(x)
#define mp make_pair
#define print(x) cout << x << endl
#define vprint(x)                         \
    rep(i, x.size()) cout << x[i] << ' '; \
    cout << endl
#define vsum(x) accumulate(all(x), 0LL)
#define vmax(a) *max_element(all(a))
#define vmin(a) *min_element(all(a))
#define lb(c, x) distance((c).begin(), lower_bound(all(c), (x)))
#define ub(c, x) distance((c).begin(), upper_bound(all(c), (x)))
// functions
// gcd(0, x) fails.
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll lcm(ll a, ll b) { return a / gcd(a, b) * b; }
template <class T>
bool chmax(T &a, const T &b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <class T>
bool chmin(T &a, const T &b) {
    if (b < a) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T>
T mypow(T x, ll n) {
    T ret = 1;
    while (n > 0) {
        if (n & 1) (ret *= x);
        (x *= x);
        n >>= 1;
    }
    return ret;
}
ll modpow(ll x, ll n, const ll mod) {
    ll ret = 1;
    while (n > 0) {
        if (n & 1) (ret *= x);
        (x *= x);
        n >>= 1;
        x %= mod;
        ret %= mod;
    }
    return ret;
}
template <typename T>
uint64_t my_rand(void) {
    static uint64_t x = 88172645463325252ULL;
    x = x ^ (x << 13);
    x = x ^ (x >> 7);
    return x = x ^ (x << 17);
}
int popcnt(ull x) { return __builtin_popcountll(x); }
// graph template
template <typename T>
struct edge {
    int src, to;
    T cost;

    edge(int to, T cost) : src(-1), to(to), cost(cost) {}

    edge(int src, int to, T cost) : src(src), to(to), cost(cost) {}

    edge &operator=(const int &x) {
        to = x;
        return *this;
    }

    bool operator<(const edge<T> &r) const { return cost < r.cost; }

    operator int() const { return to; }
};
template <typename T>
using Edges = vector<edge<T>>;
template <typename T>
using WeightedGraph = vector<Edges<T>>;
using UnWeightedGraph = vector<vector<int>>;
struct Timer {
    clock_t start_time;
    void start() { start_time = clock(); }
    int lap() {
        // return x ms.
        return (clock() - start_time) * 1000 / CLOCKS_PER_SEC;
    }
};
/* #endregion*/
// constant
#define inf 1000000005
#define INF 8000000004000000000LL
#define mod 1000000007LL
//#define endl '\n'
typedef modint<mod> mint;
const long double eps = 0.000000000000001;
const long double PI = 3.141592653589793;
// library

int main() {
    cin.tie(0);
    ios::sync_with_stdio(0);
    int n;
    cin >> n;
    vl a(n * 3 + 2);
    rep(i, n * 3) {
        cin >> a[i];
        a[i]--;
    }
    a[n * 3] = n;
    a[n * 3 + 1] = n;

    mat<ll> dp(n + 1, vl(n + 1, -inf));
    vl mx(n + 1, -inf);

    dp[a[0]][a[1]] = 0;
    dp[a[1]][a[0]] = 0;
    mx[a[0]] = 0;
    mx[a[1]] = 0;

    ll lazy = 0;
    rep(i, n) {
        vl s = {a[i * 3 + 2], a[i * 3 + 3], a[i * 3 + 4]};
        unordered_map<ll, unordered_map<ll, ll>> tmp;
        sort(all(s));
        if (s[0] == s[1] && s[1] == s[2]) {
            lazy++;
        } else if (s[0] == s[1] || s[1] == s[2]) {
            int b = s[0];
            int c = s[2];
            if (c == s[2]) swap(b, c);

            rep(j, n + 1) {
                tmp[j][b] = mx[j];
                tmp[j][c] = mx[j];
                chmax(tmp[j][c], tmp[j][b] + 1);

                chmax(tmp[b][b], mx[j]);
                chmax(tmp[b][c], mx[j]);
                chmax(tmp[c][b], mx[j]);
            }
            chmax(tmp[b][b], dp[c][c] + 1);
            chmax(tmp[b][c], dp[b][b] + 1);
            chmax(tmp[c][b], dp[b][b] + 1);
        } else {
            //一枚だけ変更
            rep(j, 3) {
                rep(k, n + 1) chmax(tmp[k][s[j]], mx[k]);
                //人組プラス
                rep(k, 3) {
                    if (j == k) continue;
                    chmax(tmp[s[k]][s[j]], dp[s[k]][s[k]] + 1);
                }
            }
            //二枚とも変更
            rep(j, 3) {
                rep(k, 3) {
                    if (j == k) continue;
                    rep(l, n + 1) chmax(tmp[s[j]][s[k]], mx[l]);
                    // 1組プラス
                    chmax(tmp[s[j]][s[k]], dp[3 - j - k][3 - j - k] + 1);
                }
            }
        }
        for (auto [x, line] : tmp) {
            for (auto [y, value] : line) {
                chmax(dp[x][y], value);
                chmax(mx[x], dp[x][y]);
            }
        }
    }
    print(lazy + vmax(mx));
}