#include <bits/stdc++.h>
using namespace std;
const int N = 200005, INF = 0X3F3F3F3F;
int n, lg[N], ind, len, g[10][10], fa[10], id[10][10], cnt[10];
char s[10];
int rds() {
  scanf("%s", s);
  return strlen(s);
}
struct ufs {
  int fa[N];
  int fd(int k1) { return fa[k1] == k1 ? k1 : fa[k1] = fd(fa[k1]); }
  void init(int n) {
    for (int i = (1); i <= (n); ++i) fa[i] = i;
  }
} o;
bool chk() {
  o.init(len);
  int tt = 0;
  for (int i = (1); i <= (len); ++i) {
    if (fa[i]) {
      o.fa[o.fd(i)] = o.fd(fa[i]);
    } else
      ++tt;
  }
  if (tt != 1) return 0;
  for (int i = (1); i <= (len); ++i)
    if (o.fd(i) != o.fd(1)) {
      return 0;
    }
  return 1;
}
int S, T;
struct maxFlow {
  int pp, lnk[1005], cur[1005], nxt[1005], to[1005], we[1005];
  int dep[1005], gap[1005];
  void clear() {
    for (int i = (1); i <= (ind); ++i) lnk[i] = 0;
    pp = 1;
  }
  void ae(int k1, int k2, int k3) {
    to[++pp] = k2, nxt[pp] = lnk[k1], we[pp] = k3, lnk[k1] = pp;
    to[++pp] = k1, nxt[pp] = lnk[k2], we[pp] = 0, lnk[k2] = pp;
  }
  int sap(int k1, int k2) {
    if (k1 == T) return k2;
    int v = k2;
    for (int& i = cur[k1]; i; i = nxt[i])
      if (we[i] && dep[k1] == dep[to[i]] + 1) {
        int f = sap(to[i], min(v, we[i]));
        we[i] -= f, we[i ^ 1] += f, v -= f;
        if (!v) return k2;
      }
    if (!--gap[dep[k1]++]) dep[S] = T + 1;
    ++gap[dep[k1]], cur[k1] = lnk[k1];
    return k2 - v;
  }
  void sol() {
    int res = 0;
    for (int i = (1); i <= (T); ++i) cur[i] = lnk[i], dep[i] = 0, gap[i] = 0;
    gap[0] = T;
    while (dep[S] <= T) {
      res += sap(S, INF);
    }
    if (res == n - 1 - len + 1) {
      vector<vector<int> > node(len + 1);
      vector<int> sym(len + 1);
      for (int i = (1); i <= (n); ++i) {
        node[lg[i] + 1].push_back(i);
      }
      for (int i = (1); i <= (len); ++i) {
        sym[i] = node[i].back();
        node[i].pop_back();
      }
      for (int i = (1); i <= (len); ++i) {
        if (fa[i]) {
          printf("%d %d\n", sym[i], sym[fa[i]]);
        }
      }
      for (int k1 = (1); k1 <= (len); ++k1) {
        for (int i = lnk[k1]; i; i = nxt[i])
          if (to[i] != S) {
            for (int j = (1); j <= (len); ++j)
              if (to[i] == id[k1][j]) {
                int tt = INF - we[i];
                while (tt--) {
                  assert(((int)(node[k1]).size()));
                  printf("%d %d\n", sym[j], node[k1].back());
                  node[k1].pop_back();
                }
              }
          }
      }
      exit(0);
    }
  }
} mf;
void work() {
  mf.clear();
  for (int i = (1); i <= (len); ++i) {
    mf.ae(S, i, cnt[i] - 1);
  }
  for (int i = (1); i <= (len); ++i) {
    for (int j = (1); j <= (len); ++j) {
      mf.ae(i, id[i][j], INF);
    }
  }
  for (int i = (1); i <= (len); ++i)
    for (int j = (i); j <= (len); ++j) mf.ae(id[i][j], T, g[i][j]);
  mf.sol();
}
void sol() {
  bool flg = 1;
  for (int i = (1); i <= (len); ++i) {
    if (fa[i]) {
      int k1 = i, k2 = fa[i];
      if (k1 > k2) swap(k1, k2);
      if (--g[k1][k2] < 0) flg = 0;
    }
  }
  if (flg) {
    work();
  }
  for (int i = (1); i <= (len); ++i) {
    if (fa[i]) {
      int k1 = i, k2 = fa[i];
      if (k1 > k2) swap(k1, k2);
      ++g[k1][k2];
    }
  }
}
void dfs(int k1) {
  if (k1 > len) {
    if (chk()) {
      sol();
    }
    return;
  }
  for (int i = (0); i <= (len); ++i) {
    fa[k1] = i;
    dfs(k1 + 1);
  }
}
int main() {
  for (int i = (10); i <= (N - 1); ++i) lg[i] = lg[i / 10] + 1;
  scanf("%d", &n);
  len = lg[n] + 1, ind = len;
  for (int i = (1); i <= (n); ++i) ++cnt[lg[i] + 1];
  for (int i = (1); i <= (len); ++i)
    for (int j = (i); j <= (len); ++j) {
      id[i][j] = ++ind;
    }
  S = ++ind, T = ++ind;
  for (int i = (1); i <= (len); ++i)
    for (int j = (1); j <= (i - 1); ++j) {
      id[i][j] = id[j][i];
    }
  for (int i = (1); i <= (n - 1); ++i) {
    int k1 = rds(), k2 = rds();
    if (k1 > k2) swap(k1, k2);
    ++g[k1][k2];
  }
  dfs(1);
  puts("-1");
  return 0;
}
