5
5 3
2 4
3 1
4 5
 4
2 4
3 2
2 1
 5
5 3
2 4
3 1
4 5
 2
1 2
 4
3 4
2 3
3 1
 10
8 10
2 9
7 8
4 1
7 3
4 7
2 5
2 4
4 6
 5
3 4
3 2
3 1
1 5
 3
1 2
2 3
 5
1 2
1 3
1 4
1 5

1 2
2 3
2 4
 1 3
2 4
3 5
4 5
 1 2
 1 3
2 3
3 4
 1 4
2 4
2 5
2 9
3 7
4 6
4 7
7 8
8 10
 1 3
1 5
2 3
3 4
 1 2
2 3
 1 2
1 3
1 4
1 5

#include <bits/stdc++.h>
using namespace std;
void make_request(int query, int vertex) {
  cout << "?"
       << " " << vertex << endl;
  cout.flush();
}
int main() {
  ios::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  int n;
  cin >> n;
  bool visited[n + 1];
  int query = ceil((float)n / 2);
  set<pair<int, int>> s;
  int f = 1;
  for (int v = 1; v <= query; v++) {
    make_request(query, f);
    vector<int> adj[n + 1];
    for (int i = 1; i <= n; i++) {
      int a;
      cin >> a;
      adj[f].push_back(a);
    }
    int x;
    for (auto it = adj[f].begin(); it != adj[f].end(); it++) {
      if (*it == 1) {
        if (s.find({it - adj[f].begin() + 1, f}) == s.end()) {
          s.insert({f, it - adj[f].begin() + 1});
          visited[it - adj[f].begin() + 1] = true;
        }
      }
      if (!visited[it - adj[f].begin() + 1]) {
        if (*it == 2) {
          x = it - adj[f].begin() + 1;
        }
      }
    }
    f = x;
    if (s.size() == n) {
      cout << "!" << endl;
      break;
    }
  }
  for (auto it : s) {
    cout << it.first << " " << it.second << endl;
  }
  return 0;
}
