#include <iostream>
#include <string.h>
using namespace std;
const int N = 1010;
// 定义物品个数和背包体积,以及每个物品的对应的体积和价值,定义成全局可以直接初始化为0
int n, V, v[N], w[N];
// 1. dp[i][j]表示从1~i个物品中选取,背包容量为j的所有选择中,背包所能装的最大价值
// 2. dp[i][j]表示从1~i个物品中选取,背包容量恰好等于j的所有选择中,背包所能装的最大价值
int dp[N][N];
int main()
{
// 读入数据
cin >> n >> V;
for (int i = 1; i <= n; i++)
cin >> v[i] >> w[i];
// 1. 解决第一问,背包不需要装满的最大价值
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= V; j++)
{
// dp[i][j]可由两种状态推导而来,选取i物品和不选取i物品
// 1. 背包一定存在不选取i物品的情况,所以可直接把最大价值暂时赋值为dp[i-1][j](从0~(i-1)个物品中选取,背包容量为j的最大价值)
dp[i][j] = dp[i - 1][j];
// 2. j-v[i]>=0则表示容量为j可以装下i物品,即存在选取i物品的状态,加上i物品的价值
// 选取i物品后,背包容量j减去i物品体积,再到1~(i-1)的物品中,容量为j-v[i]的情况下找最大
if (j - v[i] >= 0) dp[i][j] = max(dp[i][j], dp[i - 1][j - v[i]] + w[i]);
}
}
cout << dp[n][V] << endl;
// 2. 解决第二问,背包恰好装满的最大价值
memset(dp, 0, sizeof(dp)); // 清空dp表
for (int j = 1; j <= V; j++)
dp[0][j] = -1; // dp表第一行初始化为-1,表示物品为0时,无法恰好装满背包容量j
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= V; j++)
{
dp[i][j] = dp[i - 1][j];
// 与第一问一致,但需要在加上一个条件判断dp[i-1][j-v[i]]是否是存在能恰好装满的
if (j - v[i] >= 0 && dp[i - 1][j - v[i]] != -1)
dp[i][j] = max(dp[i][j], dp[i - 1][j - v[i]] + w[i]);
}
}
// dp表最后一个位置有可能是不能够恰好装满容量为V的背包,需特判一下
cout << (dp[n][V] == -1 ? 0 : dp[n][V]) << endl;
return 0;
}