#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> P;
typedef vector<ll> vl;
typedef vector<vector<ll>> vvl;
typedef vector<vector<vector<ll>>> vvvl;
struct edge{ll to,cost;};
const int inf = 1<<28;
const ll INF = 1LL<<53;
const int COMBMAX = 500005;
const ll MOD = 1e9+7;
#define rep(i,n) for(int i=0;i<n;++i)
#define repf(i,n) for(int i=0;i<=n;++i)
#define rep1(i,n) for(int i=1;i<=n;++i)
#define eachdo(v, e) for(const auto& v : e)
#define all(v) (v).begin(), (v).end()
#define lower_index(v, e) (ll)distance(v.begin(), lower_bound((v).begin(), (v).end(), e))
#define upper_index(v, e) (ll)distance(v.begin(), upper_bound((v).begin(), (v).end(), e))
template<class T1, class T2> ll bcount(T1 v, T2 a){return upper_index(v,a)-lower_index(v,a);} 
template<class T1, class T2>ll mpow(T1 a, T2 n, ll mod=MOD){ll res=1;while(n>0){if(n&1)res=res*a%mod;a=a*a%mod;n>>=1;}return res;}
template<class T> bool chmin(T &a, T b){if(a>b){a=b;return true;}return false;}
template<class T> bool chmax(T &a, T b){if(a<b){a=b;return true;}return false;}
template<class T> void debug(T v){rep(i,v.size()) cout<<v[i]<<" ";cout<<endl;}
template<class T> void debug2(T v){rep(i,v.size()){rep(j,v[i].size())cout<<v[i][j]<<" ";cout<<endl;}}
template<class T> void rdv(T &v){rep(i,v.size())cin>>v[i];}
template<class T> void rdvv(T &v){rep(i,v.size()){rep(j,v[i].size())cin>>v[i][j];}}
struct Graph{
public:
ll V;
vector<vector<edge>> G;
vector<vector<ll>> Gmatrix;
Graph(ll NodeNum){
    V = NodeNum;
    G.resize(V);
}
    void add(ll from,ll to,ll cost = 1){
        G[from].push_back({to,cost});
    }

    void UpdateGmatrix() {
        Gmatrix.resize(V);
        rep(i,V){
            Gmatrix[i].resize(V,INF);
            eachdo(e,G[i]){
                Gmatrix[i][e.to] = e.cost;
            }
        }
    }

    vector<vector<ll>> WarshallFloyd(){
        UpdateGmatrix();
        vector<vector<ll>> dist(V,vector<ll>(V,INF));
        rep(i,V) dist[i][i] = 0;
        rep(k,V)rep(i,V)rep(j,V) chmin(dist[i][j],dist[i][k]+dist[k][j]);
        return dist;
    }

    vector<ll> Dijkstra(ll start){
        vector<ll> dist(V,INF);
        priority_queue<P,vector<P>,greater<P>> que;
        dist[start] = 0;
        que.push(P(0,start)); 
        while(!que.empty()){
            P p = que.top();que.pop();
            ll v = p.second;
            if(dist[v]<p.first) continue;
            rep(i,G[v].size()){
                edge e = G[v][i];
                if(dist[v] + e.cost < dist[e.to]){
                    dist[e.to] = dist[v]+e.cost;
                    que.push(P(dist[e.to],e.to));
                }
            }
        }
        return dist;
    }
};

class UnionFind {
public:
    vector <ll> par;
    vector <ll> siz;

    UnionFind(ll sz_): par(sz_), siz(sz_, 1LL) {
        for (ll i = 0; i < sz_; ++i) par[i] = i;
    }
    void init(ll sz_) {
        par.resize(sz_);
        siz.assign(sz_, 1LL);
        for (ll i = 0; i < sz_; ++i) par[i] = i;
    }

    ll find(ll x) {
        while (par[x] != x) {
            x = par[x] = par[par[x]];
        }
        return x;
    }

    bool merge(ll x, ll y) {
        x = find(x);
        y = find(y);
        if (x == y) return false;
        if (siz[x] < siz[y]) swap(x, y);
        siz[x] += siz[y];
        par[y] = x;
        return true;
    }

    bool issame(ll x, ll y) {
        return find(x) == find(y);
    }

    ll size(ll x) {
        return siz[find(x)];
    }
};

struct SegmentTree{
private:
    ll unit = 0;
    ll op(ll a,ll b){return a + b;}
    ll n;
    vector<ll> node;

public:
SegmentTree(vector<ll> v){
    ll sz = (ll)v.size();
    n = 1;while(n<sz) n *= 2;
    node.resize(2*n-1,unit);
    for(ll i=0;i<sz;i++) node[i+n-1]=v[i];
    for(ll i=n-2;i>=0;i--) node[i]=op(node[2*i+1],node[2*i+2]);
}

    void update(ll x,ll val){
        x += n-1;
        node[x] += val;
        while(x>0){
            x = (x-1)/2;
            node[x] = op(node[2*x+1],node[2*x+2]);
        }
    }

    ll query(ll a,ll b,ll k = 0,ll l = 0,ll r = -1){
        if (r<0) r = n;
        if (r<=a || b<=l) return unit;
        if (a<=l && r<=b) return node[k];
        ll vl = query(a,b,2*k+1,l,(l+r)/2);
        ll vr = query(a,b,2*k+2,(l+r)/2,r);
        return op(vl,vr);
    }
};

struct LazySegmentTree{
private:
    ll n;
    vector<ll> node,lazy;
public:
LazySegmentTree(vector<ll> v){
    ll sz=(ll)v.size();
    n=1;while(n<sz) n*=2;
    node.resize(2*n-1);
    lazy.resize(2*n-1);
    for(ll i=0;i<sz;i++) node[i+n-1]=v[i];
    for(ll i=n-2;i>=0;i--) node[i]=node[2*i+1]+node[2*i+2];
}

    void lazyEval(ll k,ll l,ll r){
        if(lazy[k]!=0){
            node[k]+=lazy[k];
            if(r-l>1){
                lazy[2*k+1]+=lazy[k]/2;
                lazy[2*k+2]+=lazy[k]/2;
            }
            lazy[k]=0;
        }
    }
    void update(ll a,ll b,ll x,ll k=0,ll l=0,ll r=-1){
        if(r<0) r=n;
        lazyEval(k,l,r);
        if(b<=l || r<=a) return;
        if(a<=l && r<=b){
            lazy[k]+=(r-l)*x;
            lazyEval(k,l,r);
        }
        else{
            update(a,b,x,2*k+1,l,(l+r)/2);
            update(a,b,x,2*k+2,(l+r)/2,r);
            node[k]=node[2*k+1]+node[2*k+2];
        }
    }

    ll query(ll a,ll b,ll k = 0,ll l = 0,ll r = -1){
        if(r<0) r=n;
        if(b<=l || r<=a) return 0; 

        lazyEval(k,l,r);
        if(a<=l && r<=b) return node[k];
        ll vl=query(a,b,2*k+1,l,(l+r)/2);
        ll vr=query(a,b,2*k+2,(l+r)/2,r);
        return vl+vr;
    }

    ll at(ll a){return query(a,a+1);}
};

struct mint {
ll x;
    mint(ll x=0):x((x%MOD+MOD)%MOD){}
    mint& operator+=(const mint a) {if ((x += a.x) >= MOD) x -= MOD;return *this;}
	mint& operator-=(const mint a) {if ((x += MOD-a.x) >= MOD) x -= MOD;return *this;}
	mint& operator*=(const mint a) {(x *= a.x) %= MOD;return *this;}
	mint operator+(const mint a) const {mint res(*this);return res+=a;}
	mint operator-(const mint a) const {mint res(*this);return res-=a;}
	mint operator*(const mint a) const {mint res(*this);return res*=a;}
	mint pow(ll t) const {if (!t) return 1;mint a = pow(t>>1);a *= a;if (t&1) a *= *this;return a;}
	mint inv() const {return pow(MOD-2);}
	mint& operator/=(const mint a) {return (*this) *= a.inv();}
	mint operator/(const mint a) const {mint res(*this);return res/=a;}
};

struct combination {
    vector<mint> fact, ifact;
    combination(int n):fact(n+1),ifact(n+1) {
        assert(n < MOD);
    	fact[0] = 1;
    	for (int i = 1; i <= n; ++i) fact[i] = fact[i-1]*i;
    	ifact[n] = fact[n].inv();
		for (int i = n; i >= 1; --i) ifact[i-1] = ifact[i]*i;
	}
	mint operator()(int n, int k) {
		if (k < 0 || k > n) return 0;
        if (COMBMAX < n){
            mint ret = 1;
            rep1(i,k){
                ret *= (n-i+1);
                ret /= i;
            }
            return ret;
        }
		return fact[n]*ifact[k]*ifact[n-k];
	}
} ;//com(COMBMAX);

int main(){
    ll N;cin >> N;
    vl a(N);rdv(a);
    sort(all(a));
    ll idx = lower_index(a,a[N-1]/2);
    if(N==2){
        cout << a[1] << " " << a[0] << endl;
        return 0;
    }
    if (idx==0)
        cout << a[N-1] << " " << a[0];
    else{
        if ((a[N-1]/2-a[idx-1])<(a[idx]-a[N-1]/2)){
            cout << a[N-1] << " " << a[idx-1] << endl; 
        }else{
            cout << a[N-1] << " " << a[idx] << endl; 
        }
    }
}

Measured memory_bytes: 1024