import java.io.PrintWriter
import java.util.*
import java.math.*
import java.lang.*
import kotlin.comparisons.*

val pw = PrintWriter(System.out)
val MOD: Long = (1e+9 + 7).toLong()

fun main(args: Array<String>) {
	val (n, bagWeight) = readListOfInt()
	val wv = Array(4) { mutableListOf<Long>() }
	val (fw, fv) = readListOfLong()
	val minW = fw.toInt()
	wv[fw.toInt()- minW].add(fv)
	for(i in 0 until n-1) {
		val (w, v) = readListOfLong()
		wv[w.toInt()-minW].add(v)
	}
	
	wv.forEach {
		it.sortDescending()
		it.add(0, 0)
	}

	var ans = 0L
	for(n0 in wv[0].indices) {
		for(n1 in wv[1].indices) {
			for(n2 in wv[2].indices) {
				for(n3 in wv[3].indices) {
					val w0 = (minW + 0L) * n0
					val w1 = (minW + 1L) * n1
					val w2 = (minW + 2L) * n2
					val w3 = (minW + 3L) * n3
					if(w0 + w1 + w2 + w3 > bagWeight) continue 
					val v0 = wv[0].subList(0, n0 + 1).sum()
					val v1 = wv[1].subList(0, n1 + 1).sum()
					val v2 = wv[2].subList(0, n2 + 1).sum()
					val v3 = wv[3].subList(0, n3 + 1).sum()
					ans = Math.max(ans, v0 + v1 + v2 + v3)
				}
			}
		}
	}
	println(ans)
	pw.flush()
}

/****** Declared Functions and Data Structures ******/
// IO
fun read() = readLine()!!
fun readInt() = read().toInt()
fun readLong() = read().toLong()
fun readDouble() = read().toDouble()
fun readListOfString() = read().split(" ")
fun readListOfInt() = readListOfString().map { it.toInt() }
fun readListOfLong() = readListOfString().map { it.toLong() }
fun readListOfDouble() = readListOfString().map { it.toDouble() }

fun Double.format(digits: Int) = String.format("%.${digits}f", this)
fun Float.format(digits: Int) = String.format("%.${digits}f", this)

fun print(value: Any) { pw.print(value) }
fun println(value : Any) { pw.println(value) }

// Extensions
fun<T> List<T>.toBuckets(): Map<T, Int>
	= this.groupBy { it }.mapValues { it.value.size }

fun<T: Comparable<T>> List<T>.upperBound(element: T, fromIndex: Int=0, toIndex: Int=this.size): Int {
	var l = fromIndex
	var r = toIndex
	while(l < r) {
		val mid = (l+r) / 2
		if(element >= this[mid]) { 
			l = mid + 1
		} else {
			r = mid
		}
	}
	return l
}

fun<T: Comparable<T>> List<T>.lowerBound(element: T, fromIndex: Int=0, toIndex: Int=this.size): Int {
	var l = fromIndex
	var r = toIndex
	while(l < r) {
		val mid = (l+r) / 2
		if(element <= this[mid]) { 
			r = mid
		} else {
			l = mid + 1
		}
	}
	return r 
}

fun <K, V> Map<K, V>.toMutableMap(): MutableMap<K, V> = HashMap(this)

// Util Functions
fun Long.makeDivisors(): List<Long> {
	val divisors = mutableListOf<Long>()
	for(i in 1L .. Math.sqrt(this.toDouble()).toLong()+1L) {
		if(this % i == 0L) {
			divisors.add(i)
			if(i != this/i) {
				divisors.add(this/i)
			}
		}
	}
	return divisors.distinct().sorted()
}

fun Long.isPrime(): Boolean {
	if(this==1L) return false
	for(i in 2L .. Math.sqrt(this.toDouble()).toLong() + 1L) {
		if(this % i == 0L && this != i) {
			return false
		}
	}
	return true
}


fun<T> permutations(src: List<T>): List<List<T>> {
	if(src.size == 1) return listOf(src)
	val perms = mutableListOf<List<T>>()
	val insertElement = src[0]
	permutations(src.drop(1)).forEach { perm ->
		for(i in 0..perm.size) {
			val newPerm = perm.toMutableList()
			newPerm.add(i, insertElement)
			perms.add(newPerm.toList())
		}
	}
	return perms
}

// return nCr, if you want nCr, please access v[n][r]
fun combinations(n: Long): List<List<Long>> {
	val v = (0..n).map { (0..n).map{0L}.toMutableList() }.toMutableList()
	for(i in 0 until v.size) {
		v[i][0] = 1L
		v[i][i] = 1L
	}
	for(i in 0 until v.size) {
		for(j in 1 until i) {
			v[i][j] = (v[i-1][j-1] + v[i-1][j]) % MOD
		}
	}
	return v.map { it.toList() }.toList()
}

fun repeatedModPow(a: Long, n: Long, mod: Long): Long = 
	when {
		n == 1L -> a % mod
		n % 2L == 1L -> (a * repeatedModPow(a, n-1L, mod)) % mod
		else -> {
			val tmp = repeatedModPow(a, n / 2L, mod)
			(tmp * tmp) % mod
		}
	}

// return nCr % MOD
fun modnCkWithoutArray(n: Long, k: Long, mod: Long): Long {
	var ret = 1L
	val min = Math.min(k, n-k)
	// まず分母の計算
	for(i in n downTo (n - min + 1)) {
		ret = ret * i % mod
	}
	for(i in min downTo 1) {
		ret = ret * repeatedModPow(i, (mod - 2L), mod) % mod
	}
	return ret
}

// return nCr % MOD
fun modnCk(n: Int, k: Int, mod: Long): Long {
	val factorials = Array(n+1) { 0L }.apply {
		this[0] = 1L
		if(this.size > 1) this[1] = 1L
	}
	val inverses = Array(n+1) { 0L }.apply {
		this[0] = 1L
		if(this.size > 1) this[1] = 1L
	}
	val factorialInverses = Array(n+1) { 0L }.apply {
		this[0] = 1L
		if(this.size > 1) this[1] = 1L
	}
	for(i in 2 .. n) {
		factorials[i] = (factorials[i-1] * i.toLong() % mod)
		inverses[i] = (mod - inverses[mod.toInt() % i] * (mod / i.toLong()) % mod)
		factorialInverses[i] = (factorialInverses[i-1] * inverses[i] % mod)
	}

	return factorials[n] % mod * (factorialInverses[k] * factorialInverses[n-k] % mod) % mod
}

// should be a >= b
fun gcd(a: Long, b: Long): Long = 
	if(b == 0L) a else gcd(b, a % b)

// shoud be a >= b
fun lcm(a: Long, b: Long): Long = 
	a / gcd(a, b) * b

fun sumDigits(num_: Long): Long {
	var num = num_
	var rtn: Long = 0
	while(num != 0.toLong()) {
		val tmp = num % 10
		rtn += tmp
		num /= 10
	}
	return rtn
}

fun Double.isDecimal(): Boolean = ((this - Math.floor(this)) != 0.0)

// Data Structures

class UnionFind(private val n: Int) {
	private val parent: Array<Int> = Array(n) { 0 }
	private val rank: Array<Int> = Array(n) { 0 }
	private val size: Array<Int> = Array(n) { 0 }

	init {
		for(i in 0 until n) {
			parent[i] = i
			rank[i] = 1
			size[i] = 1
		}
	}

	fun union(u: Int, v: Int) {
		var x = root(u)
		var y = root(v)
		if(x != y) {
			if(rank[y] < rank[x]) {
				val tmp = y 
				y = x
				x = tmp 
			}
			parent[x] = y
			rank[y] += rank[x]
			rank[x] = -1
			val totalSize = size[x] + size[y]
			size[x] = totalSize
			size[y] = totalSize
		}
	}

	fun root(u: Int): Int {
		var x = u
		while(parent[x] != x) {
			x = parent[x]
		}
		return x 
	}

	fun size(u: Int): Int = size[root(u)]
}


Measured memory_bytes: 46640