// Main maut ko takiya, aur kafan ko chaadar banakar audhta hoon!

#include <bits/stdc++.h>
#include <ext/pb_ds/detail/standard_policies.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>

using namespace std;
using namespace __gnu_pbds;

using ll = long long;
using ld = long double;

#define pb emplace_back
#define push emplace
#define mp make_pair
#define ff first
#define ss second
#define all(v) v.begin(), v.end()
#define len(a) (int) a.size()
#define reset(a, val) memset(a, val, sizeof(a))
// setbase - cout << setbase (16); cout << 100 << endl; Prints 64
// setfill - cout << setfill ('x') << setw (5); cout << 77 << endl; prints xxx77
#define int long long
#define vll vector<int>
#define pll pair<int, int>
#define vvll vector<vector<int>>
#define vpll vector<pair<int, int>>
mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
// shuffle(a.begin(), a.end(), rng) or shuffle(a, a+n, rng) instead of random_shuffle
// uniform_int_distribution<int/ld>(l, r)(rng) for generating random integer/double numbers in [l, r]

#ifdef ONLINE_JUDGE
    #define endl '\n'
    #pragma comment(linker, "/stack:268435456")
    #pragma GCC optimize ("O3")
    #pragma GCC optimize ("O2")
    #pragma GCC optimize("Ofast")
    #pragma GCC optimize ("unroll-loops")
    #pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
    #define _CRT_SECURE_NO_WARNINGS
#endif

#ifndef ONLINE_JUDGE
#define trace(...) __f(#__VA_ARGS__, __VA_ARGS__)
    template <typename Arg1>
    void __f(const char* name, Arg1&& arg1) { cerr << name << " : " << arg1 << endl; }
    template <typename Arg1, typename... Args>
    void __f(const char* names, Arg1&& arg1, Args&&... args) {
        const char* comma = strchr(names + 1, ',');
        cerr.write(names, comma - names) << " : " << arg1 << "  ";
        __f(comma + 1, args...);
    }
#else
#define trace(...)
#endif // ifndef ONLINE_JUDGE

struct custom_hash {
    static int splitmix64(int x) {
        x += 0x9e3779b97f4a7c15; x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9; x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
        return x ^ (x >> 31);
    }

    size_t operator()(int x) const {
        static const int FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count();
        return splitmix64(x + FIXED_RANDOM);
    }
};
template <class T, class cmp = less<T>>
using ordered_set = tree<T, null_type, cmp, rb_tree_tag, tree_order_statistics_node_update>;
#define unordered_map fast_unordered_map
template <class Key, class Value, class Hash = std::hash<Key>> // Hash = custom_hash for ints
using fast_unordered_map = gp_hash_table<Key, Value, Hash>;
#define unordered_set fast_unordered_set
template <class Key, class Hash = std::hash<Key>> // Hash = custom_hash for ints
using fast_unordered_set = gp_hash_table<Key, null_type, Hash>;

template <typename T> ostream& operator << (ostream& os, const vector<T>& v) { for (int i = 0; i < v.size(); ++i) os << v[i] << " "; return os; } 
template <typename T> ostream& operator << (ostream& os, const set<T>& v) { for (auto &it : v) os << it << " "; return os; } 
template <typename T, typename S> ostream& operator << (ostream& os, const pair<T, S>& v) { os << v.first << " " << v.second; return os; } 
template <typename T, typename S> ostream& operator << (ostream& os, const map<T, S>& v) { for (auto &it : v) os << "(" << it.first << "=>" << it.second << ")" << endl; return os; } 
template <typename T, typename S> ostream& operator << (ostream& os, const unordered_map<T, S>& v) { for (auto &it : v) os << "(" << it.first << "=>" << it.second << ")" << endl; return os; } 

const int mod = 1e9 + 7;
const int inf = 2e18;
const int ninf = -2e18;

int power(int x, int y, int mod = inf) 
{
    int ans = 1; 
    x %= mod;
    while(y > 0) 
    {
        if(y % 2) ans = (x * ans) % mod;
        x = (x * x) % mod;
        y >>= 1;
    }
    return ans;
}

// 1 based items. Returns maximum value that can fit in a bag.
// v[i] is value of i'th item, w[i] is wt. of i'th item.
// solve(n, w) if maximum cost to get weight <= w from the first i items.

const int N = 1e2 + 10;
// const int Wt = 1e5 + 10;
// int dp[N][Wt] = {-1};
int v[N], w[N];

// int solve(int n, int w)
// {
// 	if(n < 0 || w < 0) return ninf;
// 	if(n == 0 or w == 0) return 0;
// 	if(dp[n][w] != -1) return dp[n][w];

// 	return dp[n][w] = max(solve(n-1, w), solve(n-1, w-w[n]) + v[n]);
// }

signed main() 
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    time_t t1, t2;
    t1 = clock();

    int n, W;
    cin >> n >> W;

    for(int i = 1; i <= n; i++) cin >> w[i] >> v[i];

    // cout << solve(n, W);

    int dp[W+1] = {0};

    for(int i = 1; i <= n; i++) 
    {
        for(int j = W-w[i]; j >= 0; j--)
        {
            dp[j+w[i]] = max(dp[j+w[i]], dp[j] + v[i]);
        }
    }

    cout << dp[W];
    


    t2 = clock();
    #ifndef ONLINE_JUDGE
        cerr << endl << "time taken: " << t2 - t1 << endl;
    #endif // ONLINE_JUDGE

    return 0;
}

Measured memory_bytes: 1024