import std.algorithm;
import std.bigint;
import std.bitmanip;
import std.concurrency : generator = Generator, yield;
import std.container;
import std.conv;
import std.functional;
import std.format;
import std.math;
import std.meta;
import std.numeric;
import std.random;
import std.range;
import std.stdio;
import std.string;
import std.traits;
import std.typecons;

alias Testimony = Tuple!(long, long);

void main() {
    long N = readln.chomp.to!long;

    Testimony[][] testimony;
    foreach (a; 0 .. N) {
        Testimony[] testimony_of_a;
        long A = readln.chomp.to!long;
        foreach (i; 0 .. A) {
            auto t = readln.chomp.split.map!(to!long);
            testimony_of_a ~= tuple(t[0], t[1]);
        }
        testimony ~= testimony_of_a;
    }

    long ans;
    for (long c = 0; c < (1 << N); c++) {
    long[] honests = iota(N).picks(scanbits(c, N)).array;
        if (isValid(honests, testimony))
            ans = ans.max(honests.length);
    }

    writeln(ans);
}

bool isValid(long[] honests, Testimony[][] testimony) {
    foreach (h; honests) {
        foreach (t; testimony[h]) {
            auto target = t[0] - 1;
            auto type = t[1];
            if (type == 0 && honests.canFind(target))
                return false;
            if (type == 1 && !honests.canFind(target))
                return false;
        }
    }
    return true;
}

auto scanbits(long x, long length) {
    return iota(length).map!(i => ((x >> i) & 1) == 1);
}

auto picks(R1, R2)(R1 range, R2 flags) {
    return zip(range, flags).filter!"a[1]".map!"a[0]";
}