#include <bits/stdc++.h>
using namespace std;
const int Max_N(1050);
namespace io {
const int SIZE = (1 << 21) + 1;
char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - 1, c,
                                       qu[55];
int f, qr;
inline void flush() {
  fwrite(obuf, 1, oS - obuf, stdout);
  oS = obuf;
}
inline void putc(char x) {
  *oS++ = x;
  if (oS == oT) flush();
}
template <class I>
inline void gi(I &x) {
  for (f = 1, c = (iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, SIZE, stdin),
                              (iS == iT ? EOF : *iS++))
                            : *iS++);
       c < '0' || c > '9';
       c = (iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, SIZE, stdin),
                       (iS == iT ? EOF : *iS++))
                     : *iS++))
    if (c == '-') f = -1;
  for (x = 0; c <= '9' && c >= '0';
       c = (iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, SIZE, stdin),
                       (iS == iT ? EOF : *iS++))
                     : *iS++))
    x = x * 10 + (c & 15);
  x *= f;
}
template <class I>
inline void print(I x) {
  if (!x) putc('0');
  if (x < 0) putc('-'), x = -x;
  while (x) qu[++qr] = x % 10 + '0', x /= 10;
  while (qr) putc(qu[qr--]);
}
struct Flusher_ {
  ~Flusher_() { flush(); }
} io_flusher_;
}  // namespace io
using io::gi;
using io::print;
using io::putc;
int N, Cnt[1 << 16];
inline unsigned long long int cut(unsigned long long int S) {
  return S & ((1ULL << 16ULL) - 1ULL);
}
inline int popcount(const unsigned long long int &S) {
  return Cnt[cut(S)] + Cnt[cut(S >> 16ULL)] + Cnt[cut(S >> 32ULL)] +
         Cnt[cut(S >> 48ULL)];
}
struct Bitset {
  unsigned long long int B[(Max_N >> 6) + 5];
  inline void set(int pos) {
    B[pos >> 6] |= (1ULL << (static_cast<unsigned long long int>(pos) & 63ULL));
  }
  inline bool qry(int pos) const {
    return B[pos >> 6] &
           (1ULL << (static_cast<unsigned long long int>(pos) & 63ULL));
  }
  inline int popcount() const {
    int Ret(0);
    for (int i = 0; i <= (N >> 6); ++i) Ret += ::popcount(B[i]);
    return Ret;
  }
  inline bool have(const Bitset &b) const {
    for (int i = 0; i <= (N >> 6); ++i)
      if ((B[i] & b.B[i]) != b.B[i]) return false;
    return true;
  }
  Bitset &operator&=(const Bitset &b) {
    for (int i = 0; i <= (N >> 6); ++i) B[i] &= b.B[i];
    return *this;
  }
  Bitset operator&(const Bitset &b) const {
    Bitset c(*this);
    c &= b;
    return c;
  }
  inline bool operator!=(const Bitset &b) const {
    for (int i = 0; i <= (N >> 6); ++i)
      if (B[i] != b.B[i]) return true;
    return false;
  }
};
Bitset Near[Max_N], Dis1[Max_N], notleaf;
inline Bitset operator-(const Bitset &a, const Bitset &b) {
  Bitset c;
  for (int i = 0; i <= (N >> 6); ++i) c.B[i] = a.B[i] - b.B[i];
  return c;
}
bool notleaf_1() {
  for (int i = 1; i <= N; ++i)
    if (Near[i].popcount() < N) return false;
  for (int i = 2; i <= N; ++i) print(1), putc(' '), print(i), putc('\n');
  return true;
}
bool notleaf_2() {
  int Cnt = 0;
  for (int i = 1; i <= N; ++i)
    if (Near[i].popcount() == N) ++Cnt;
  if (Cnt != 2) return false;
  Bitset Find = Near[1];
  int A = 0, B = 0, goA = 0, goB = 0;
  for (int i = 1; i <= N; ++i) {
    Find &= Near[i];
    if (Near[i].popcount() < N)
      if (goA == 0)
        goA = i;
      else if (Near[i] != Near[goA])
        if (goB == 0)
          goB = i;
        else if (Near[i] != Near[goB])
          return false;
  }
  if (goA == 0 || goB == 0 || Find.popcount() != 2) return false;
  for (int u = 1; u <= N; ++u)
    if (Find.qry(u))
      if (A == 0)
        A = u;
      else
        B = u;
  print(A), putc(' '), print(B), putc('\n');
  for (int i = 1; i <= N; ++i)
    if (i != A && i != B && Near[goA].qry(i))
      print(A), putc(' '), print(i), putc('\n');
  for (int i = 1; i <= N; ++i)
    if (i != A && i != B && Near[goB].qry(i))
      print(B), putc(' '), print(i), putc('\n');
  return true;
}
int main() {
  for (int S = 0; S < (1 << 16); ++S)
    for (int i = 0; i < 16; ++i) Cnt[S] += ((S & (1 << i)) != 0);
  gi(N);
  if (N == 2) {
    print(1), putc(' '), print(2), putc('\n');
    return 0;
  }
  for (int i = 1, k; i <= N; ++i) {
    gi(k);
    for (int u; k--;) gi(u), Near[i].set(u);
  }
  if (notleaf_1()) return 0;
  if (notleaf_2()) return 0;
  Bitset temp;
  for (int i = 1; i <= N; ++i)
    for (int j = i; j <= N; ++j)
      if (((temp = Near[i]) &= Near[j]).popcount() == 2) {
        int p, q;
        p = q = 0;
        for (int x = 0, u; x <= (N >> 6); ++x)
          if (temp.B[x])
            for (unsigned long long int b = 0ULL; b <= 63ULL; ++b)
              if (temp.B[x] & (1ULL << b)) {
                u = (x << 6) + b;
                if (p == 0)
                  p = u;
                else
                  q = u;
              }
        if (Dis1[p].qry(q)) continue;
        Dis1[p].set(q), Dis1[q].set(p), print(p), putc(' '), print(q),
            putc('\n'), notleaf.set(p), notleaf.set(q), Dis1[p].set(p),
            Dis1[q].set(q);
      }
  for (int i = 1; i <= N; ++i)
    if (!notleaf.qry(i)) {
      int Min = 0X3F3F3F3F;
      Bitset temp;
      for (int j = 1; j <= N; ++j)
        if (Near[j].qry(i)) Min = min(Min, (Near[j] & notleaf).popcount());
      for (int j = 1; j <= N; ++j)
        if (Near[j].qry(i) && (Near[j] & notleaf).popcount() == Min)
          temp = (Near[j] & notleaf);
      for (int j = 1; j <= N; ++j)
        if (temp.qry(j) && !(Dis1[j] != temp)) {
          print(i), putc(' '), print(j), putc('\n');
          break;
        }
    }
  return 0;
}
