using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
class Problem
{
	static readonly int Inf = 1073741789;	// 2 * Inf < int.MaxValue, and Inf is a prime number
	private int N;
	private int[] items;
	public int Size { get { return N; } }
	public Problem()
	{
		using (var sc = new Scan(true))
		{
			sc.Read(out N);
			items = Func.Range(0, N).Select(x => sc.ReadInt()).ToArray();
		}
	}
	private int[] memo2;
	public void Solve()
	{
		memo2 = new int[N];
		for (var n = 0; n < N; n++) memo2[n] = Inf;
		for (var n = 0; n < N; n++)
		{
			var k = Func.FirstBinary(0, N, x => items[n] <= memo2[x]);
			memo2[k] = items[n];
		}
		Console.WriteLine(Func.FirstBinary(0, N, x => memo2[x] == Inf));
	}
}
class Program
{
	static void Main()
	{
		int num = 1;
		decimal time = 0;
		for (var tmp = 0; tmp < num; tmp++)
		{
			var P = new Problem();
			time += Func.MeasureTime(() => P.Solve());
		}
	}
	static IEnumerable<Point> Neighbors(Point point)
	{
		yield return new Point(point.X - 1, point.Y);
		yield return new Point(point.X, point.Y - 1);
		yield return new Point(point.X, point.Y + 1);
		yield return new Point(point.X + 1, point.Y);
	}
}
class PairComparer<S, T> : IComparer<Pair<S, T>>
	where S : IComparable<S>
	where T : IComparable<T>
{
	public PairComparer() { }
	public int Compare(Pair<S, T> x, Pair<S, T> y)
	{
		var p = x.First.CompareTo(y.First);
		if (p != 0) return p;
		else return x.Second.CompareTo(y.Second);
	}
}
class Pair<S, T>
{
	public S First;
	public T Second;
	public Pair() { First = default(S); Second = default(T); }
	public Pair(S s, T t) { First = s; Second = t; }
	public override string ToString() { return "(" + First + ", " + Second + ")"; }
	public override int GetHashCode() { return First.GetHashCode() ^ Second.GetHashCode(); }
	public override bool Equals(object obj)
	{
		if (ReferenceEquals(this, obj)) return true;
		else if (obj == null) return false;
		var tmp = obj as Pair<S, T>;
		return (object)tmp != null && First.Equals(tmp.First) && Second.Equals(tmp.Second);
	}
}
class Point : Pair<int, int>
{
	public int X { get { return First; } set { First = value; } }
	public int Y { get { return Second; } set { Second = value; } }
	public Point() : base(0, 0) { }
	public Point(int x, int y) : base(x, y) { }
}
class Scan : IDisposable
{
	private TextReader file;
	public Scan(bool useConsole) { file = useConsole ? Console.In : new StreamReader("input.txt"); }
	public void Dispose() { file.Dispose(); }
	public int ReadInt() { return int.Parse(file.ReadLine()); }
	public uint ReadUInt() { return uint.Parse(file.ReadLine()); }
	public long ReadLong() { return long.Parse(file.ReadLine()); }
	public double ReadDouble() { return double.Parse(file.ReadLine()); }
	public decimal ReadDecimal() { return decimal.Parse(file.ReadLine()); }
	public string ReadString() { return file.ReadLine(); }
	public Pair<S, T> ReadPair<S, T>() { S s; T t; Read(out s, out t); return new Pair<S, T>(s, t); }
	public Point ReadPoint() { int s, t; Read(out s, out t); return new Point(s, t); }
	public void Read<S>(out S s)
	{
		var read = ReadMulti(Type.GetTypeCode(typeof(S))).ToArray();
		s = (S)read[0];
	}
	public void Read<S, T>(out S s, out T t)
	{
		var read = ReadMulti(Type.GetTypeCode(typeof(S)), Type.GetTypeCode(typeof(T))).ToArray();
		s = (S)read[0];
		t = (T)read[1];
	}
	public void Read<S, T, U>(out S s, out T t, out U u)
	{
		var read = ReadMulti(Type.GetTypeCode(typeof(S)), Type.GetTypeCode(typeof(T)), Type.GetTypeCode(typeof(U))).ToArray();
		s = (S)read[0];
		t = (T)read[1];
		u = (U)read[2];
	}
	public void Read<S, T, U, V>(out S s, out T t, out U u, out V v)
	{
		var read = ReadMulti(Type.GetTypeCode(typeof(S)), Type.GetTypeCode(typeof(T)), Type.GetTypeCode(typeof(U)), Type.GetTypeCode(typeof(V))).ToArray();
		s = (S)read[0];
		t = (T)read[1];
		u = (U)read[2];
		v = (V)read[3];
	}
	private IEnumerable<object> ReadMulti(params TypeCode[] types)
	{
		var inp = file.ReadLine().Split(' ');
		for (var i = 0; i < types.Length; i++)
		{
			if (types[i] == TypeCode.Int32) yield return int.Parse(inp[i]);
			else if (types[i] == TypeCode.UInt32) yield return uint.Parse(inp[i]);
			else if (types[i] == TypeCode.Int64) yield return long.Parse(inp[i]);
			else if (types[i] == TypeCode.Double) yield return double.Parse(inp[i]);
			else if (types[i] == TypeCode.Decimal) yield return decimal.Parse(inp[i]);
			else if (types[i] == TypeCode.String) yield return inp[i];
			else throw new Exception();
		}
	}
	public T[,] ReadBoard<T>(int X, int Y, Func<char, int, int, T> selector)
	{
		var array = new T[X, Y];
		for (var y = 0; y < Y; y++)
		{
			var str = ReadString();
			for (var x = 0; x < X; x++) array[x, y] = selector(str[x], x, y);
		}
		return array;
	}
}
static class Func
{
	/// <summary>
	/// Find the first number x such that pred(x) is true
	/// if pred(x) is false for all min&lt;=x&lt;max, then return max
	/// in other words, pred(max) is assumed to be true
	/// </summary>
	/// <param name="min">inclusive lower limit</param>
	/// <param name="max">exclusive upper limit</param>
	/// <param name="pred">monotonous predicate, i.e. if pred(a) and a&lt;b, then pred(b)</param>
	/// <returns>first number such that satisfy pred</returns>
	public static long FirstBinary(long min, long max, Predicate<long> pred)
	{
		var left = min;
		var right = max;
		while (left < right)
		{
			var mid = (left + right) >> 1;
			if (pred(mid)) right = mid;
			else left = mid + 1;
		}
		return left;
	}
	public static void Swap<T>(this T[] array, int i, int j) { var tmp = array[i]; array[i] = array[j]; array[j] = tmp; }
	public static void Swap<T>(this List<T> array, int i, int j) { var tmp = array[i]; array[i] = array[j]; array[j] = tmp; }
	public static IEnumerable<int> Range(int min, int max) { for (var i = min; i < max; i++) yield return i; }
	public static IEnumerable<T> Reverse<T>(this IEnumerable<T> source)
	{
		var list = source.ToArray();
		for (var i = list.Length - 1; i >= 0; i++) yield return list[i];
	}
	public static T IndexAt<T>(this T[,] array, Pair<int, int> index) { return array[index.First, index.Second]; }
	public static bool InRegion(this Pair<int, int> p, int X, int Y) { return p.InRegion(0, X, 0, Y); }
	public static bool InRegion(this Pair<int, int> p, int x, int X, int y, int Y) { return p.First >= x && p.Second >= y && p.First < X && p.Second < Y; }
	/// <summary>
	/// get all permutation of 0, 1, ..., n - 1
	/// </summary>
	/// <param name="n">length of array</param>
	/// <param name="func">if you want to change the elements of the array, you must take a copy</param>
	public static void Permutation(int n, Action<int[]> func)
	{
		var array = new int[n];
		var unused = new bool[n];
		for (var i = 0; i < n; i++) unused[i] = true;
		Permutation(n, 0, array, unused, func);
	}
	private static void Permutation(int n, int i, int[] array, bool[] unused, Action<int[]> func)
	{
		if (i == n) func(array);
		else
			for (var x = 0; x < n; x++)
				if (unused[x])
				{
					array[i] = x;
					unused[x] = false;
					Permutation(n, i + 1, array, unused, func);
					unused[x] = true;
				}
	}
	public static long Fact(int n)
	{
		var fact = 1L;
		for (var i = 2; i <= n; i++) fact *= i;
		return fact;
	}
	public static long LCM(long n, long m) { return Math.Abs((n / GCD(n, m)) * m); }
	public static long Divide(long n, long m) { return (n - Remainder(n, m)) / m; }
	public static long Remainder(long n, long m)
	{
		if (m == 0) throw new DivideByZeroException();
		else if (m < 0) return Remainder(n, -m);
		else
		{
			var r = n % m;
			return r < 0 ? r + m : r;
		}
	}
	public static long GCD(long n, long m)
	{
		var a = Math.Abs(n);
		var b = Math.Abs(m);
		if (a < b) { var c = a; a = b; b = c; }
		while (b > 0)
		{
			var c = a % b;
			a = b;
			b = c;
		}
		return a;
	}
	public static decimal MeasureTime(Action action)
	{
		action();return 0;
	}
}

Measured memory_bytes: 11232