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

import java.io.*;
import java.util.*;

public class Main
{
    static BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
    static StreamTokenizer st=new StreamTokenizer(br);
    static PrintWriter pw=new PrintWriter(new OutputStreamWriter(System.out), true);

    static int nextInt() throws IOException
    {
        st.nextToken();
        return (int)st.nval;
    }

    static String nextStr() throws IOException
    {
        st.nextToken();
        return st.sval;
    }

    static int brReadInt() throws IOException
    {
        return Integer.parseInt(br.readLine());
    }

    static long brReadLong() throws IOException
    {
        return Long.parseLong(br.readLine());
    }

    static int[] brReadIntArray(int n) throws IOException
    {
        int[] res=new int[n];
        String[] input=br.readLine().split(" ");
        for(int i=0; i<n; i++)
            res[i]=Integer.parseInt(input[i]);
        return res;
    }

    static long[] brReadLongArray(int n) throws IOException
    {
        long[] res=new long[n];
        String[] input=br.readLine().split(" ");
        for(int i=0; i<n; i++)
            res[i]=Long.parseLong(input[i]);
        return res;
    }

    static int gcd(int a, int b)
    {
        return b==0 ? a : gcd(b, a%b);
    }

    static int INF=0x3f3f3f3f;

    static int[] ask(int x, int n) throws IOException
    {
        System.out.println("? "+x);
        System.out.println();
        System.out.flush();
        int[] ans=new int[n];
        for(int j=0; j<n; j++)
            ans[j]=nextInt();
        return ans;
    }

    public static void main(String[] args) throws IOException
    {
        int n=nextInt();

        Queue<Integer> queue=new ArrayDeque<>();
        queue.add(1);
        boolean[] vis=new boolean[n+1];
        vis[1]=true;
        List<int[]> ans=new ArrayList<>();
        while(!queue.isEmpty())
        {
            int x=queue.remove();
            int[] res=ask(x, n);
            for(int i=0; i<n; i++)
            {
                if(res[i]==1 && !vis[i+1])
                {
                    vis[i+1]=true;
                    queue.add(i+1);
                    ans.add(new int[]{x, i+1});
                }
            }
        }

        System.out.println("!");
        for(int[] edge : ans)
            System.out.println(edge[0]+" "+edge[1]);
    }

}


class BinaryIndexTree
{
    long[] tree;
    int len;

    BinaryIndexTree(int n)
    {
        len=n;
        tree=new long[n];
    }

    private int LowBit(int x)
    {
        return x&(-x);
    }

    public void update(int x, int k)
    {
        while(x<len)
        {
            tree[x]+=k;
            x+=LowBit(x);
        }
    }

    public long query(int x)
    {
        long sum=0;
        while(x>0)
        {
            sum+=tree[x];
            x-=LowBit(x);
        }
        return sum;
    }
}

class HideArray
{
    int[] preSum;

    HideArray(int[] nums)
    {
        int n=nums.length;
        preSum=new int[n+1];
        for(int i=1; i<=n; i++)
            preSum[i]=preSum[i-1]+nums[i-1];
    }

    int getPreSum(int right)
    {
        return preSum[right];
    }
}