#include <bits/stdc++.h>
using namespace std;
const int __SIZE = 1 << 18;
char ibuf[__SIZE], *iS, *iT;
template <typename T>
inline void read(T &x) {
  char ch, t = 0;
  x = 0;
  while (!isdigit(ch = (iS == iT
                            ? (iT = (iS = ibuf) + fread(ibuf, 1, __SIZE, stdin),
                               (iS == iT ? EOF : *iS++))
                            : *iS++)))
    t |= ch == '-';
  while (isdigit(ch))
    x = x * 10 + (ch ^ 48),
    ch = (iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, __SIZE, stdin),
                      (iS == iT ? EOF : *iS++))
                   : *iS++);
  x = t ? -x : x;
}
inline char read_alpha() {
  char c = (iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, __SIZE, stdin),
                        (iS == iT ? EOF : *iS++))
                     : *iS++);
  while (!isalpha(c) && c != EOF)
    c = (iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, __SIZE, stdin),
                     (iS == iT ? EOF : *iS++))
                  : *iS++);
  return c;
}
inline int read_int() {
  int x;
  return read(x), x;
}
inline long long read_ll() {
  long long x;
  return read(x), x;
}
template <typename T>
inline void chkmin(T &a, T b) {
  a = a < b ? a : b;
}
template <typename T>
inline void chkmax(T &a, T b) {
  a = a > b ? a : b;
}
const int MAXN = 1010;
bitset<MAXN> b[MAXN], tmp;
bitset<MAXN> E[MAXN], U;
int used[MAXN];
struct Edge {
  int u, v;
  Edge() {}
  Edge(int u, int v) : u(u), v(v) {}
};
vector<Edge> G;
int main() {
  int n = read_int();
  for (int i = 1; i <= n; i++) {
    int d = read_int();
    while (d--) b[i].set(read_int());
  }
  int ct = 0;
  for (int i = 1; i <= n; i++) {
    for (int j = i + 1; j <= n; j++) {
      tmp = b[i] & b[j];
      if (tmp.count() == 2) {
        int u = 0, v = 0;
        for (int k = 1; k <= n; k++) tmp[k] ? (u ? v = k : u = k) : 0;
        if (!E[u][v]) G.push_back(Edge(u, v)), ++ct;
        E[u][v] = E[v][u] = used[u] = used[v] = 1;
      }
    }
  }
  if (ct == 0) {
    for (int i = 2; i <= n; i++) printf("1 %d\n", i);
  } else if (ct == 1) {
    for (int i = 1; i <= n; i++) {
      if (b[i].count() != n) {
        tmp = b[i];
        break;
      }
    }
    printf("%d %d\n", G[0].u, G[0].v);
    for (int i = 1; i <= n; i++)
      if (!used[i]) printf("%d %d\n", i, tmp[i] ? G[0].u : G[0].v);
  } else {
    for (int i = 1; i <= n; i++)
      if (!used[i]) U.set(i);
    bitset<MAXN> to, nd;
    for (int i = 1; i <= n; i++) {
      if (used[i]) {
        E[i][i] = 1;
        if (E[i].count() <= 2) continue;
        int flag = 0;
        for (int j = 1; j <= n; j++)
          if ((b[j] & E[i]) == E[i])
            if (flag)
              nd &= b[j];
            else
              nd = b[j], flag = 1;
        if (flag) {
          for (int j = 1; j <= n; j++)
            if (nd[j] && !used[j]) G.push_back(Edge(i, j)), used[j] = 2;
        }
        used[i] = 2;
      }
    }
    for (int i = 1; i <= n; i++) {
      int x = 0;
      for (int j = 1; j <= n; j++) {
        if (used[j] == 2 && b[i][j]) b[i][j] = 0;
        if (used[j] == 1 && b[i][j]) {
          if (x)
            x = -1;
          else
            x = j, b[i][j] = 0;
        }
      }
      if (x > 0) {
        for (int j = 1; j <= n; j++) {
          if (b[i][j]) {
            used[j] = 2;
            G.push_back(Edge(x, j));
          }
        }
      }
    }
    for (auto e : G) printf("%d %d\n", e.u, e.v);
  }
  return 0;
}
