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

/*
stream Butter!
eggyHide eggyVengeance
I need U
xiao rerun when
 */
import static java.lang.Math.max;
import static java.lang.Math.min;
import static java.lang.Math.abs;
import static java.lang.System.out;
import java.util.*;
import java.io.*;
import java.math.*;

public class x1534D
{
    static final long CONS = 420420L;
    public static void main(String hi[]) throws Exception
    {
        BufferedReader infile = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(infile.readLine());
        int N = Integer.parseInt(st.nextToken());
        System.out.println("? 1");
        int[] initial = readArr(N, infile, st);
        HashSet<Long> edges = new HashSet<Long>();
        int oddcnt = 0;
        for(int v=0; v < N; v++)
        {
            if(initial[v] == 1)
                edges.add(pair(1, v+1));
            oddcnt += (initial[v]&1);
        }
        int evencnt = N-oddcnt;
        ArrayList<Integer> sources = new ArrayList<Integer>();
        if(oddcnt == evencnt) //N is even, odd = even
        {
            for(int v=1; v < N; v++)
                if(initial[v]%2 == 0)
                    sources.add(v+1);
        }
        else if(oddcnt > evencnt)
        {
            for(int v=1; v < N; v++)
                if(initial[v]%2 == 0)
                    sources.add(v+1);
        }
        else
        {
            for(int v=1; v < N; v++)
                if(initial[v]%2 == 1)
                    sources.add(v+1);
        }
        for(int root: sources)
        {
            System.out.println("? "+root);
            int[] dists = readArr(N, infile, st);
            for(int v=0; v < N; v++)
                if(dists[v] == 1)
                    edges.add(pair(root, v+1));
        }
        StringBuilder sb = new StringBuilder();
        for(long x: edges)
            sb.append((x/CONS)+" "+(x%CONS)+"\n");
        System.out.println("!");
        System.out.print(sb);
        System.out.flush();
    }
    public static long pair(int a, int b)
    {
        if(a > b)
            a = (a+b)-(b=a);
        return CONS*a+b;
    }
    public static int[] readArr(int N, BufferedReader infile, StringTokenizer st) throws Exception
    {
        int[] arr = new int[N];
        st = new StringTokenizer(infile.readLine());
        for(int i=0; i < N; i++)
            arr[i] = Integer.parseInt(st.nextToken());
        return arr;
    }
}
/*
Think about 2-coloring of tree (bipartite)
An edge from a to b exists if and only if dist(a, b) = 1
Every edge consists of one black and one white node

 */