#include<bits/stdc++.h>
#include<cassert>
#include<numeric>

using namespace std;
typedef long long ll;
typedef long double ld;
using graph = vector<vector<int>>;
using pii = pair<int,int>;
using pll =pair<ll,ll>;
using qi =queue<int>;
using qpii =queue<pii>;
using pqi =priority_queue<int>;
using pqr = priority_queue<int,vector<int>, greater<int>>;

#define rep(i,n) for(ll i=0;i<(ll)(n);i++)
#define repd(i,n) for(ll i=n-1;i>=0;i--)
#define F(i,a,b) for(ll i=a;i<=(ll)(b);i++)
#define FD(i,a,b) for(ll i=a;i>=(ll)(b);i--)
#define sd(x) sort(x.rbegin(),x.rend())
//xにはvectorなどのコンテナ
#define all(x) x.begin(),x.end()
#define size(x) ((ll)(x).size())
#define ma(x) *max_element(all(x))
#define mi(x) *min_element(all(x))
#define so(x) sort(all(a));

#define INF 1000000000000
#define M 10000007
#define MAXR 100000

#define pb push_back
#define mp make_pair
#define f first
#define s second
#define fi(x,y) find(all(x),y)!=x.end()
#define dfi(x,y) find(all(x),y)==x.end()
template<class T> inline bool chmax(T &a,T b) { if (a<b) { a=b; return true; } return false; }
template<class T> inline bool chmin(T &a,T b) { if (b<a) { a=b; return true; } return false; }
template<class T> inline int rt(T a,T b){if(a%b==0)return a/b;else return a/b+1;}

int gcd(int a,int b){return b?gcd(b,a%b):a;}
int dx[4]={1,0,-1,0};
int dy[4]={0,1,0,-1};
#define bit(n) (1LL<<(n))
#define U(v) v.erase( unique(v.begin(), v.end()), v.end() );

#define dump(x)  cerr << #x << " = " << (x) << endl;
#define debug(x) cerr << #x << " = " << (x) << " (L" << __LINE__ << ")" << " " << __FILE__ << endl;

#define fe(x,a) for(auto& (x) : (a) )
#define vin(x) for(auto&youso_: (x) )cin>>youso_
#define vout(x) for(auto&youso_: (x) )cout<<youso_<<" "
#define sco(x) cout<<(x)<<" "
#define co(x) cout<<(x)<<endl
#define en cout<<endl
#define gl goto la
#define r0 return 0
void CINT(){}
template <class Head,class... Tail>
void CINT(Head&& head,Tail&&... tail){
    cin>>head;
    CINT(move(tail)...);
}

#define vi vector<int>
#define vll vector<ll>
#define vd vector<double>
#define vld vector<ld>
#define vs vector<string>

#define ci(...) int __VA_ARGS__;CINT(__VA_ARGS__)
#define cd(...) double __VA_ARGS__;CINT(__VA_ARGS__)
#define cld(...) ld __VA_ARGS__;CINT(__VA_ARGS__)
#define cll(...) ll __VA_ARGS__;CINT(__VA_ARGS__)
#define cs(...) string __VA_ARGS__;CINT(__VA_ARGS__)



struct uf {
    vector<int> par; // par[i]:iの親の番号　(例) par[3] = 2 : 3の親が2
    vector<int> rcount;

    uf(int N) : par(N) { //最初は全てが根であるとして初期化
        for(int i = 0; i < N; i++) par[i] = i;
    }

    int root(int x) { // データxが属する木の根を再帰で得る：root(x) = {xの木の根}
        if (par[x] == x) return x;
        return par[x] = root(par[x]);
    }

    void unite(int x, int y) { // xとyの木を併合
        int rx = root(x); //xの根をrx
        int ry = root(y); //yの根をry
        if (rx == ry) return; //xとyの根が同じ(=同じ木にある)時はそのまま
        par[rx] = ry; //xとyの根が同じでない(=同じ木にない)時：xの根rxをyの根ryにつける
    }

    bool same(int x, int y) { // 2つのデータx, yが属する木が同じならtrueを返す
        int rx = root(x);
        int ry = root(y);
        return rx == ry;
    }

};
template<int MOD> struct Fp {
    long long val;
    constexpr Fp(long long v = 0) noexcept : val(v % MOD) {
        if (val < 0) val += MOD;
    }
    constexpr int getmod() { return MOD; }
    constexpr Fp operator - () const noexcept {
        return val ? MOD - val : 0;
    }
    constexpr Fp operator + (const Fp& r) const noexcept { return Fp(*this) += r; }
    constexpr Fp operator - (const Fp& r) const noexcept { return Fp(*this) -= r; }
    constexpr Fp operator * (const Fp& r) const noexcept { return Fp(*this) *= r; }
    constexpr Fp operator / (const Fp& r) const noexcept { return Fp(*this) /= r; }
    constexpr Fp& operator += (const Fp& r) noexcept {
        val += r.val;
        if (val >= MOD) val -= MOD;
        return *this;
    }
    constexpr Fp& operator -= (const Fp& r) noexcept {
        val -= r.val;
        if (val < 0) val += MOD;
        return *this;
    }
    constexpr Fp& operator *= (const Fp& r) noexcept {
        val = val * r.val % MOD;
        return *this;
    }
    constexpr Fp& operator /= (const Fp& r) noexcept {
        long long a = r.val, b = MOD, u = 1, v = 0;
        while (b) {
            long long t = a / b;
            a -= t * b; swap(a, b);
            u -= t * v; swap(u, v);
        }
        val = val * u % MOD;
        if (val < 0) val += MOD;
        return *this;
    }
    constexpr bool operator == (const Fp& r) const noexcept {
        return this->val == r.val;
    }
    constexpr bool operator != (const Fp& r) const noexcept {
        return this->val != r.val;
    }
    friend constexpr ostream& operator << (ostream &os, const Fp<MOD>& x) noexcept {
        return os << x.val;
    }
    friend constexpr Fp<MOD> modpow(const Fp<MOD> &a, long long n) noexcept {
        if (n == 0) return 1;
        auto t = modpow(a, n / 2);
        t = t * t;
        if (n & 1) t = t * a;
        return t;
    }
};
const ll MOD = 1000000007;
using mint = Fp<MOD>;
mint powm(ll a, ll b){mint re = 1;rep(i, b)re *= a;return re;}


int main(){
	ci(n);
	vector<vector<pii>>p(n);
	int ans=0;
	rep(i,n){
		ci(a);
		rep(j,a){
			ci(b,c);
			p[i].pb(mp(b-1,c));
		}
	}
	bool flag=true;
	rep(bit,1<<n){
		flag=true;
		rep(i,n){
			if(bit&(1<<i)){
				rep(j,size(p[i])){
//					dump(bit)
//					dump(i)
//					dump(j)
//					dump(p[i][j].f)
//					dump(p[i][j].s)
//					dump(bit&1<<p[i][j].f)
					if((bit&1<<p[i][j].f)==(p[i][j].s<<p[i][j].f)){
						continue;
					}
					else flag=false;
				}
			}
		}
//		dump(flag)
		if(flag){
			int tmp=0;
			rep(i,n){
				if(bit&(1<<i))tmp++;
			}
			chmax(ans,tmp);
		}
//		en;
	}
	co(ans);
}



Measured memory_bytes: 3572