#include <bits/stdc++.h>
using namespace std;

using ll = long long;
const int INF = INT_MAX;//2147483647
const int MOD = (int)1e9 + 7;
const double EPS = 1e-9;
#ifdef LOCAL_ENV
	#define debug(var) std::cout<<#var" = "<<var<<std::endl
#else
	#define debug(var)
#endif
#define p(var)  std::cout<<var<<std::endl
#define PI (acos(-1))
#define rep(i, n)   for(int i=0, i##_length=(n); i< i##_length; ++i)
#define repeq(i, n) for(int i=1, i##_length=(n); i<=i##_length; ++i)
#define all(a) (a).begin(), (a).end()
#define pb push_back
inline double isnatural(double a) { return a>=0 && ceil(a)==floor(a); }
template<typename T> inline T gcd(T a, T b) { return b ? gcd(b,a%b) : a; }
template<typename T> inline T lcm(T a, T b) { return a / gcd(a,  b) * b; }
template<typename T1, typename T2>inline bool chmax(T1 &a, T2 b) { return a < b && (a = b, true); }
template<typename T1, typename T2>inline bool chmin(T1 &a, T2 b) { return a > b && (a = b, true); }
template<typename T1, typename T2> ostream& operator<<(ostream& s, const pair<T1, T2>& p) {return s << "(" << p.first << ", " << p.second << ")";}
template<typename T> ostream& operator<<(ostream& s, const vector<T>& v) {
	for (int i = 0, len = v.size(); i < len; ++i){
		s << v[i]; if (i < len - 1) s << "\t";
	}
	return s;
}
template<typename T> ostream& operator<<(ostream& s, const vector< vector<T> >& vv) {
	for (int i = 0, len = vv.size(); i < len; ++i){
		s << vv[i] << endl;
	}
	return s;
}
template<typename T1, typename T2> ostream& operator<<(ostream& s, const map<T1, T2>& m) {
	s << "{" << endl;
	for (typeof(m.begin()) itr = m.begin(); itr != m.end(); ++itr){
		s << "\t" << (*itr).first << " : " << (*itr).second << endl;
	}
	s << "}" << endl;
	return s;
}

/*-----8<-----8<-----*/

class Item{
public:
    ll value;
    ll weight;
    bool operator<(const Item &another) const
    {
        return ((double)value/weight) < ((double)another.value/another.weight);
    };
};

ll N,W;
vector<Item> items;

vector<bool> used_items;
vector<bool> opt_items;
ll opt_value=0;

//分枝限定法で探索
//index=index-1番目の商品までは選択が終わった、index番目からの商品はまだ調べてない
//value=今のところの価値合計
//weight=今のところの重さ合計
void searchbb(int index,ll value,ll weight){
    //全部調べ終わった
    if(index==N){
        if(opt_value < value){
            opt_items=used_items;
            opt_value=value;
        }
        return;
    }

    //緩和問題を解く
    ll tmp_weight=weight;
    double tmp_value=value;
    for(int i=index;i<N;i++){
        if(tmp_weight+items[i].weight == W){
            //緩和問題の解が元の問題でも実現可能→暫定解として使えるかも
            tmp_value+=items[i].value;
            if(opt_value<tmp_value){
                opt_value=tmp_value;
                opt_items=used_items;
                for(int j=index;j<=i;j++){
                    opt_items[j]=true;
                }
            }
			return;
        }else if(tmp_weight+items[i].weight < W){
            tmp_value+=items[i].value;
            tmp_weight+=items[i].weight;
        }else{
            //重さの余りを詰める
            tmp_value+=(double)items[i].value*(W-tmp_weight)/items[i].weight;
            tmp_weight=W;
            break;
        }
    }

    //緩和問題ですら暫定解を超えられない→これ以上調査する意味がない
    if(tmp_value < opt_value){
        return;
    }

    //重量制限が許されれば、index番目を選ぶ場合で探索
    if(weight+items[index].weight <= W){
        used_items[index]=true;
        searchbb(index+1, value+items[index].value, weight+items[index].weight);
    }
    //index番目を選ばない場合で探索
    used_items[index]=false;
    searchbb(index+1, value, weight);
}

int main(){
    cin >> N >> W;
	items=vector<Item>(N,Item());
    rep(i,N){
        Item item;
        cin >> items[i].weight;
        cin >> items[i].value;
    }

    //コスパのよい順に並び替え
    sort(all(items), [](const Item &x, const Item &y) {
        return ((double)x.value/x.weight) > ((double)y.value/y.weight);
    });

    //暫定解として貪欲に選んでみる
    ll tmp_weight=0,tmp_value=0;
    vector<bool> tmp_items(N,false);
    rep(i,N){
        if(tmp_weight+items[i].weight <= W){
            tmp_value+=items[i].value;
            tmp_weight+=items[i].weight;
            tmp_items[i]=true;
        }else{
            break; 
        }
    }
    opt_items=tmp_items;
    opt_value=tmp_value;

    //分枝限定法で探索
    used_items=vector<bool>(N,false);
    searchbb(0,0,0);

    cout << opt_value << endl;

    return 0;
    
}






Measured memory_bytes: 256