# n,v=map(int,input().split())
# dp=[0]*(v+1)
# for i in range(1,n+1):
# val, wig=map(int,input().split())
# for j in range(v,wig-1,-1):
# dp[j]=max(dp[j],dp[j-wig]+val)
# print(dp[v])
# 动态规划解决0-1背包问题
N = 1010 # 定义最大容量和物品数量的限制
n, m = map(int, input().split()) # 输入物品数量 n 和背包容量 m
f = [0] * N # 初始化动态规划数组 f,大小为 N
for i in range(n): # 遍历每个物品
v, w = map(int, input().split()) # 输入当前物品的体积 v 和价值 w
for j in range(m, v - 1, -1): # 从后往前更新背包容量
f[j] = max(f[j], f[j - v] + w)
print(f[m]) # 输出最终结果,即背包容量为 m 时的最大价值
#v为体积数组,wig为价值数组
#原先的递推式是f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+wig[i])
#要注意的是[j-v[i]]表示保证能装下第i件物品,所以留给前i-1件物品的最大容量为[j-v[i]]
#f[i-1][j-v[i]]表示保证能装下第i件物品的前提下,前i-1件物品的最大值。
#前i个物品and当背包最大容量为j时的最大价值=(最大容量为j时and不拿第i件物品的最大价值,最大容量为j时and拿了第i件物品的最大价值)
#正常按照上述递推式需要n行m列的数组 => 第i(1~n)行遍历j列(0~m) 表示前i件物品and最大容量为j时的最大价值(f[i][j])
#由递推式子可知前i件物品的价值只与前i-1件有关,(也就是说当前第i行只与上一轮(i-1)行有关),
#所以,我们可以尝试每次计算完第i行的第j列时,将其对i-1行j列实时覆盖,使得可以每次操作都只在一行内进行(只保留当前行,i行),
#但是如果容量是从0开始往后遍历,那么计算f[i][j]时,式子中的f[i-1][j-v[i]]中的数据早已是被覆盖后的数据了,所以后面的数据会失真
#因此容量可以从后往前遍历(m~0),那么每次访问f[i-1][j-v[i]]中的数据时仍然为原始数据!!!!
#由此可知,只要从后往前遍历更新,就能实现对f[i-1][j-v[i]]的正确访问,此时即可真正实现只用一行进行递推演算。
#既然是一行,那么就可以用一维数组表示(不再用到i)
#