#pragma GCC optimize("Ofast")
//#define _GLIBCXX_DEBUG

#include <iostream>
#include <vector>
#include <array>
#include <string>
#include <algorithm>
#include <set>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <cmath>
#include <deque>
#include <cassert>
#include <iomanip>
#include <fstream>
#include <ctime>
#include <numeric>
#include <queue>
#include <functional>
#include <bitset>
#include <random>

#define ios ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define sz(a) (long long)((a).size())
#define fixed cout << setprecision(25) << fixed;
#define all(v) (v).begin(), (v).end()
#define rall(v) v.rbegin(), v.rend()
#define debug(x) cerr << #x << " = " << x << endl;
#define debug2(x, y) cerr << #x << " = " << x <<", "<< #y << " = " << y << endl;
#define debug3(x, y, z) cerr << #x << " = " << x << ", "<< #y << " = " << y << ", "<< #z << " = " << z << endl;
#define debug4(x, y, z, u) cerr << #x << " = " << x << ", "<< #y << " = " << y << ", "<< #z << " = " << z << ", "<< #u << " = " << u << endl;
#define debug_a(x) cerr << #x << endl; for (auto e : (x)) cerr << (ll)e << ' '; cerr << endl;
#define debug_b(x) cerr << #x << endl; for (auto e : (x)) cerr << " ( " << e.st << ' ' << e.nd << " ) "; cerr << endl;
#define debug_c(x) cerr << #x << endl; for (auto e : (x)) cerr << " ( " << e[0] << ' ' << e[1] << ' ' << e[2] << " ) "; cerr << endl;
#define pikachu push_back
#define st first
#define nd second
#define i128 __int128
#define pt(s) (s)[sz((s)) - 2]
#define pll pair<ll, ll>
#define arl array<ll, 3>

using namespace std;
typedef long ll;
typedef long double ld;
const ll MOD = 1e9 + 7;
ll n, ct = 0;

vector<ll> distance(ll v) {
    ct++;
    assert(ct <= (n + 1) / 2);
    vector<ll> ans(n + 1, 0);
    cout << "? " << v << endl;
    for (ll i = 1; i <= n; i++) cin >> ans[i];
    return ans;
}

int main() {
    cin >> n;
    auto dist = distance(1);
    set<pll > s;
    set<pll > ans;
    for (ll i = 1; i <= n; i++) {
        s.insert({-dist[i], i});
        if (dist[i] == 1) ans.insert({i, 1});
    }
    vector<ll> dobdis(n + 1, -1);
    vector<ll> par(n + 1, -1);
    while (-(*s.begin()).st > 1) {
        auto[d, v] = *s.begin();
        s.erase(s.begin());
        //debug2(dobdis[v], v);
        if (dobdis[v] != -1) {
            //debug2(dobdis[v], par[dobdis[v]]);
            ans.insert({par[dobdis[v]], v});
            continue;
        }
        auto kek = distance(v);
        vector<pll > parent;
        for (ll i = 1; i <= n; i++) {
            if (dist[i] + kek[i] == dist[v]) parent.pikachu({kek[i], i});
            if (kek[i] == 2) dobdis[i] = v;
            //debug3(kek[i], i, v);
        }
        sort(all(parent));
        if (sz(parent) > 1) par[v] = parent[1].nd;
        ll last = v;
        for (auto[ds, p]: parent) ans.insert({last, p}), last = p, s.erase({-dist[p], p});
    }
    cout << "! " << endl;
    for (auto[e1, e2]: ans) if (e1 != e2) cout << e1 << ' ' << e2 << endl;
}