using System;
using System.Text;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using E = System.Linq.Enumerable;


class Program
{
    static void Main(string[] args)
    {
        //var N = long.Parse(Console.ReadLine());
        var NT = Console.ReadLine().Split(' ').Select(x => long.Parse(x)).ToArray();
        var N = NT[0];
        var T = NT[1];

        var A = new long[N];
        var B = new long[N];
        for (int i = 0; i < N; i++)
        {
            var AB = Console.ReadLine().Split(' ').Select(x => long.Parse(x)).ToArray();
            A[i] = AB[0];
            B[i] = AB[1];
        }

        var dp = new long[N + 1, T];
        for (int i = 1; i <= N; i++)
        {
            for (int j = 0; j <= T-1; j++)
            {
                if (j >= A[i-1] && dp[i - 1, j - A[i-1]] + B[i-1] > dp[i - 1, j])
                {
                    dp[i, j] = dp[i - 1, j-A[i-1]]+B[i-1];
                }
                else
                {
                    dp[i, j] = dp[i - 1, j];
                }
            }
        }
        var C = new long[N];
        Array.Copy(B,C,N);
        Array.Reverse(A);
        Array.Reverse(B);

        var dp2 = new long[N + 1, T];
        for (int i = 1; i <= N; i++)
        {
            for (int j = 0; j <= T - 1; j++)
            {
                if (j >= A[i - 1] && dp2[i - 1, j - A[i - 1]] + B[i - 1] > dp2[i - 1, j])
                {
                    dp2[i, j] = dp2[i - 1, j - A[i - 1]] + B[i - 1];
                }
                else
                {
                    dp2[i, j] = dp2[i - 1, j];
                }
            }
        }

        var ans = 0L;
        for (int i = 1; i <= N; i++)
        {
            for (int j = 0; j < T; j++)
            {
                ans = Math.Max(ans, dp[i - 1, j] + dp2[N - i, T - j - 1] + C[i - 1]);
            }
        }
        Console.WriteLine(ans);
    }

}

public static class CharExt
{
    public static int ToInt(this char self)
    {
        return self - '0';
    }
}


class Modular
{
    private const int M = 1000000007;
    public long value;
    public Modular(long value) { this.value = value; }
    public static implicit operator Modular(long a)
    {
        var m = a % M;
        return new Modular((m < 0) ? m + M : m);
    }
    public static Modular operator +(Modular a, Modular b)
    {
        return (a.value + b.value)%M;
    }
    public static Modular operator -(Modular a, Modular b)
    {
        return a.value - b.value;
    }
    public static Modular operator *(Modular a, Modular b)
    {
        return a.value * b.value;
    }
    public static Modular Pow(Modular a, int n)
    {
        switch (n)
        {
            case 0:
                return 1;
            case 1:
                return a;
            default:
                var p = Pow(a, n / 2);
                return p * p * Pow(a, n % 2);
        }
    }
    public static Modular operator /(Modular a, Modular b)
    {
        return a * Pow(b, M - 2);
    }
    private static readonly List<int> facs = new List<int> { 1 };
    private static Modular Fac(int n)
    {
        for (int i = facs.Count; i <= n; ++i)
        {
            facs.Add((int)(Math.BigMul(facs.Last(), i) % M));
        }
        return facs[n];
    }
    public static Modular Ncr(int n, int r)
    {
        return (n < r) ? 0
             : (n == r) ? 1
                        : Fac(n) / (Fac(r) * Fac(n - r));
    }
    public static explicit operator int(Modular a)
    {
        return (int)a.value;
    }

    public static Modular Lcm(Modular a, Modular b)
    {
        var tmp = Gcdl(a.value, b.value);
        return ((a * b) / new Modular(tmp));
    }
    static Modular Gcd(Modular m, Modular n)
    {
        if (n.value > m.value) return Gcd(n,m);
        if (n.value == 0) return m;
        var r = new Modular(m.value % n.value);
        return Gcd(n,r);
    }

    static long Gcdl(long m, long n)
    {
        if (n > m) return Gcdl(n,m);
        if (n == 0) return m;
        return Gcdl(n, m % n);
    }
}
public class PriorityQueue<T> : IEnumerable<T>
{
    private readonly List<T> _data = new List<T>();
    private readonly IComparer<T> _comparer;
    private readonly bool _isDescending;

    public PriorityQueue(IComparer<T> comparer, bool isDescending = true)
    {
        _comparer = comparer;
        _isDescending = isDescending;
    }

    public PriorityQueue(Comparison<T> comparison, bool isDescending = true)
        : this(Comparer<T>.Create(comparison), isDescending)
    {
    }

    public PriorityQueue(bool isDescending = true)
        : this(Comparer<T>.Default, isDescending)
    {
    }

    public void Enqueue(T item)
    {
        _data.Add(item);
        var childIndex = _data.Count - 1;
        while (childIndex > 0)
        {
            var parentIndex = (childIndex - 1) / 2;
            if (Compare(_data[childIndex], _data[parentIndex]) >= 0)
                break;
            Swap(childIndex, parentIndex);
            childIndex = parentIndex;
        }
    }

    public T Dequeue()
    {
        var lastIndex = _data.Count - 1;
        var firstItem = _data[0];
        _data[0] = _data[lastIndex];
        _data.RemoveAt(lastIndex--);
        var parentIndex = 0;
        while (true)
        {
            var childIndex = parentIndex * 2 + 1;
            if (childIndex > lastIndex)
                break;
            var rightChild = childIndex + 1;
            if (rightChild <= lastIndex && Compare(_data[rightChild], _data[childIndex]) < 0)
                childIndex = rightChild;
            if (Compare(_data[parentIndex], _data[childIndex]) <= 0)
                break;
            Swap(parentIndex, childIndex);
            parentIndex = childIndex;
        }
        return firstItem;
    }

    public T Peek()
    {
        return _data[0];
    }

    private void Swap(int a, int b)
    {
        var tmp = _data[a];
        _data[a] = _data[b];
        _data[b] = tmp;
    }

    private int Compare(T a, T b)
    {
        return _isDescending ? _comparer.Compare(b, a) : _comparer.Compare(a, b);
    }

    public int Count => _data.Count;

    public IEnumerator<T> GetEnumerator()
    {
        return _data.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}


class UnionFind



{



    // 親要素のインデックスを保持する



    // 親要素が存在しない(自身がルートである)とき、マイナスでグループの要素数を持つ



    public long[] Parents { get; set; }



    public UnionFind(long n)



    {



        this.Parents = new long[n];



        for (int i = 0; i < n; i++)



        {



            // 初期状態ではそれぞれが別のグループ(ルートは自分自身)



            // ルートなのでマイナスで要素数(1個)を保持する



            this.Parents[i] = -1;



        }



    }







    // 要素xのルート要素はどれか



    public long Find(long x)



    {



        // 親がマイナスの場合は自分自身がルート



        if (this.Parents[x] < 0) return x;



        // ルートが見つかるまで再帰的に探す



        // 見つかったルートにつなぎかえる



        this.Parents[x] = Find(this.Parents[x]);



        return this.Parents[x];



    }







    // 要素xの属するグループの要素数を取得する



    public long Size(long x)



    {



        // ルート要素を取得して、サイズを取得して返す



        return -this.Parents[this.Find(x)];



    }







    // 要素x, yが同じグループかどうか判定する



    public bool Same(long x, long y)



    {



        return this.Find(x) == this.Find(y);



    }







    // 要素x, yが属するグループを同じグループにまとめる



    public bool Union(long x, long y)



    {



        // x, y のルート



        x = this.Find(x);



        y = this.Find(y);



        // すでに同じグループの場合処理しない



        if (x == y) return false;







        // 要素数が少ないグループを多いほうに書き換えたい



        if (this.Size(x) < this.Size(y))



        {



            var tmp = x;



            x = y;



            y = tmp;



        }



        // まとめる先のグループの要素数を更新



        this.Parents[x] += this.Parents[y];



        // まとめられるグループのルートの親を書き換え



        this.Parents[y] = x;



        return true;



    }



}



Measured memory_bytes: 153816