#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using ll = long long; using ull = unsigned long long; using PII = pair; using PLL = pair; template ostream& operator<<(ostream& s, const pair& p) { return s << "(" << p.first << ", " << p.second << ")"; } template ostream& operator<<(ostream& s, const vector& v) { s << "["; for (int i = 0; i < v.size(); i++) s << (i == 0 ? "" : ", ") << v[i]; s << "]"; return s; } #define ALL(a) (a).begin(), (a).end() using Weight = int; // 重み付きの辺 struct Edge { int from, to; Weight weight; Edge() {} Edge(int to) : from(-1), to(to), weight(-1) {} Edge(int from, int to) : from(from), to(to), weight(-1) {} Edge(int from, int to, Weight weight) : from(from), to(to), weight(weight) {} bool operator<(const Edge& e) const { return (weight != e.weight) ? (weight < e.weight) : (from < e.from); } bool operator>(const Edge& e) const { return (weight != e.weight) ? (weight > e.weight) : (from > e.from); } }; using Arc = Edge; using Graph = vector>; using DGraph = vector>; using Tree = Graph; // 最初に初期化することを忘れないように! using GArray = vector; using GMatrix = vector; // この4つの関数は頂点数を固定したバージョンでも動く void add_edge(Graph& g, int u, int v, Weight w = -1) { g[u].push_back(Edge(u, v, w)); g[v].push_back(Edge(v, u, w)); } void add_arc(DGraph& dg, int u, int v, Weight w = -1) { dg[u].push_back(Arc(u, v, w)); } const int GM_INF = (1LL << 25); void init_gmatrix(GMatrix& gm) { for (int u = 0; u < gm.size(); u++) { for (int v = 0; v < gm[u].size(); v++) { gm[u][v] = GM_INF; } } } void add_edge(GMatrix& gm, int u, int v, Weight w = -1) { gm[u][v] = w; gm[v][u] = w; } void add_arc(GMatrix& gm, int u, int v, Weight w = -1) { gm[u][v] = w; } namespace TSort { const int MAX_V = 1010; DGraph dgraph; vector nodes; int nodes_i; array used; array indeg; void bfs(int s) { queue que; que.push(s); used[s] = true; while (!que.empty()) { int v = que.front(); que.pop(); nodes[nodes_i++] = v; for (Arc a : dgraph[v]) { indeg[a.to]--; if (indeg[a.to] == 0 && !used[a.to]) { used[a.to] = true; que.push(a.to); } } } } vector& tsort(DGraph& dg) { dgraph = dg; nodes = vector(dgraph.size(), -1); used.fill(false); indeg.fill(0); nodes_i = 0; for (int v = 0; v < dgraph.size(); v++) { for (Arc a : dgraph[v]) { indeg[a.to]++; } } for (int v = 0; v < dgraph.size(); v++) { if (indeg[v] == 0 && !used[v]) { bfs(v); } } return nodes; } }; const int MAX_N = 1010; vector x, y, z; int table[MAX_N][MAX_N]; bool can_contain(int u, int v) { vector us{x[u], y[u], z[u]}, vs{x[v], y[v], z[v]}; sort(ALL(us)); sort(ALL(vs)); for (int i = 0; i < 3; i++) { if (us[i] <= vs[i]) return false; } return true; } int main() { int n; cin >> n; x.resize(n); y.resize(n); z.resize(n); DGraph dgraph(n); for (int i = 0; i < n; i++) { cin >> x[i] >> y[i] >> z[i]; } for (int u = 0; u < n; u++) { for (int v = 0; v < n; v++) { if (can_contain(u, v)) { add_arc(dgraph, u, v); } } } auto nodes = TSort::tsort(dgraph); memset(table, -1, sizeof(table)); int ans = -1; for (int i = 0; i < n; i++) { table[i][nodes[i]] = max(table[i][nodes[i]], 1); for (int j = 0; j < n; j++) { ans = max(ans, table[i][j]); if (table[i][j] < 0 || i == n - 1) continue; table[i + 1][j] = max(table[i + 1][j], table[i][j]); if (can_contain(j, nodes[i + 1])) { table[i + 1][nodes[i + 1]] = max(table[i + 1][nodes[i + 1]], table[i][j] + 1); } } } cout << ans << endl; return 0; }