import java.util.Scanner;
public class Main {
/**
* 看来大雪菜的背包九讲
* dp动态规划,注意状态转移方程的细节
* 还是不太清楚为什么压缩后不用去遍历数组
* 前面是将数组压缩为一维
* 未压缩的在下面的注释部分
*/
public static void main(String[] args) {
Scanner in=new Scanner(System.in);
int n=in.nextInt();
int v=in.nextInt();
int dp[]=new int[v+1];
int t[]=new int[n+1];//体积
int z[]=new int[n+1];//价值
for (int i = 1; i < t.length; i++) {
t[i]=in.nextInt();
z[i]=in.nextInt();
}
for (int i = 1; i <= n; i++) {
for (int j = v; j>0; j--) {//为什么压缩一维之后要反过来,因为要用到上一层的j-t[i],而从小到大的话就会丢失这个数。从大到小就完美的保留了这个数
if(j>=t[i])
dp[j]=Math.max(dp[j], dp[j-t[i]]+z[i]);
}
}
int res=0;
for (int i = 0; i < dp.length; i++) {
res=Math.max(dp[i], res);
}
System.out.println(res);
}
//
// /*
// * 背包九讲
// * dp动态规划,注意状态转移方程的细节
// * 可将数组压缩为一维
// /
// public static void main(String[] args) {
// Scanner in=new Scanner(System.in);
// int n=in.nextInt();
// int v=in.nextInt();
// int dp[][]=new int[n+1][v+1];
// int t[]=new int[n+1];
// int z[]=new int[n+1];
// for (int i = 1; i < t.length; i) {
// t[i]=in.nextInt();
// z[i]=in.nextInt();
// }
// for (int i = 1; i < dp.length; i) {
// for (int j = 1; j < dp[1].length; j) {
// if(j>=t[i])
// dp[i][j]=Math.max(dp[i-1][j], dp[i-1][j-t[i]]+z[i]);
// else
// dp[i][j]=dp[i-1][j];
// }
// }
// int res=0;
// for (int i = 0; i < dp.length; i) {
// for (int j = 0; j < dp[1].length; j) {
// System.out.print(dp[i][j]+” “);
// }
// System.out.println();
// }
// for (int i = 0; i < dp[n].length; i) {
// res=Math.max(dp[n][i], res);
// }
// System.out.println(res);
// }
}