#include <algorithm>
#include <bits/stdc++.h>
#include <cstddef>
#include <cstdint>
#include <iostream>
#include <iterator>
#include <type_traits>
#include <vector>

// lib/pow2.hpp
#ifndef POW2_HPP
#define POW2_HPP

/*
usage:

tools::pow2(5) => 32
*/


namespace tools {
  template <typename T, typename std::enable_if<std::is_unsigned<T>::value, std::nullptr_t>::type = nullptr>
  constexpr T pow2(const T x) {
    return static_cast<T>(1) << x;
  }

  template <typename T, typename std::enable_if<std::is_signed<T>::value, std::nullptr_t>::type = nullptr>
  constexpr T pow2(const T x) {
    return static_cast<T>(static_cast<typename std::make_unsigned<T>::type>(1) << static_cast<typename std::make_unsigned<T>::type>(x));
  }
}

#endif

// lib/util.hpp
#ifndef UTIL_HPP
#define UTIL_HPP


typedef ::std::int_fast64_t i64;
typedef ::std::uint_fast64_t u64;
typedef ::std::int_fast32_t i32;
typedef ::std::uint_fast32_t u32;

namespace tools {
  template <typename T>
  void read(::std::istream& is, ::std::vector<T>& vector, const typename ::std::vector<T>::size_type size) {
    vector.reserve(size);
    ::std::copy_n(::std::istream_iterator<T>(is), size, ::std::back_inserter(vector));
  }
  template <typename T>
  void read(::std::vector<T>& vector, const typename ::std::vector<T>::size_type size) {
    ::tools::read(::std::cin, vector, size);
  }

  template <typename T, ::std::size_t N>
  void read(::std::istream& is, ::std::array<T, N>& array) {
    ::std::copy_n(std::istream_iterator<T>(is), N, array.begin());
  }
  template <typename T, ::std::size_t N>
  void read(::std::array<T, N>& array) {
    ::tools::read(::std::cin, array);
  }
}

#endif

// main.cpp

enum class state {
  honest,
  unkind,
  unknown
};

class pattern {
private:
  std::vector<state> status;
public:
  pattern(const i64& N) : status(N, state::unknown) {
  }
  pattern(const i64& N, u64 status_as_u64) : status(N) {
    for (i64 i = 0; i < N; ++i) {
      switch (status_as_u64 & static_cast<u64>(1)) {
      case 0:
        this->status[i] = state::unkind;
        break;
      case 1:
        this->status[i] = state::honest;
        break;
      }
      status_as_u64 >>= 1;
    }
  }
  i64 honest_count() const {
    return std::count_if(this->status.begin(), this->status.end(), [](const state& s) { return s == state::honest; });
  }
  bool is_honest(const i64& i) const {
    return this->status[i] == state::honest;
  }
  void set_status(std::istream& is) {
    i64 A;
    is >> A;
    for (i64 i = 0; i < A; ++i) {
      i64 x, y;
      is >> x >> y;
      --x;
      switch (y) {
      case 0:
        this->status[x] = state::unkind;
        break;
      case 1:
        this->status[x] = state::honest;
        break;
      }
    }
  }
  bool matches(const pattern& other) const {
    const i64 N = this->status.size();
    if (static_cast<i64>(other.status.size()) != N) return false;
    for (i64 i = 0; i < N; ++i) {
      if (this->status[i] == state::unknown) continue;
      if (other.status[i] == state::unknown) continue;
      if (this->status[i] != other.status[i]) return false;
    }
    return true;
  }
};

int main() {
  i64 N;
  std::cin >> N;
  std::vector<pattern> clues(N, pattern(N));
  for (pattern& clue : clues) {
    clue.set_status(std::cin);
  }
  i64 answer = 0;
  for (u64 status_as_u64 = 0; status_as_u64 < tools::pow2<u64>(N); ++status_as_u64) {
    pattern hypothesis(N, status_as_u64);
    bool is_consistent = true;
    for (i64 i = 0; i < N; ++i) {
      if (hypothesis.is_honest(i) && !hypothesis.matches(clues[i])) {
        is_consistent = false;
        break;
      }
    }
    if (is_consistent) {
      answer = std::max(answer, hypothesis.honest_count());
    }
  }
  std::cout << answer << std::endl;
  return 0;
}

Measured memory_bytes: 256