# -*- coding: utf-8 -*-



"""

参考：https://img.atcoder.jp/arc073/editorial.pdf

　　　https://ei1333.hateblo.jp/entry/2017/05/01/004235

　　　https://atcoder.jp/contests/abc060/submissions/3782609

・公式解

・累積和あり

"""



import sys

def input(): return sys.stdin.readline().strip()

sys.setrecursionlimit(10 ** 9)



from collections import defaultdict



N, W = list(map(int, input().split()))

wN = [0] * N

vN = [[] for i in range(4)]

for i in range(N):

    w, v = list(map(int, input().split()))

    if i == 0:

        offset = w

    vN[w-offset].append(v)



# 重さ毎のリストを降順ソート

for i in range(4):

    # 空リストのままだとこの後ループを回ってくれないので、[0]を追加する

    vN[i] = [0] + sorted(vN[i], reverse=True)



# 累積和

for i in range(4):

    for j in range(1, len(vN[i])):

        vN[i][j] = vN[i][j-1] + vN[i][j]



ans = 0

for i in range(len(vN[0])):

    for j in range(len(vN[1])):

        for k in range(len(vN[2])):

            for l in range(len(vN[3])):

                # 重さリミットを超えるパターンは対象外

                if offset * i + (offset+1) * j + (offset+2) * k + (offset+3) * l > W:

                    continue

                ans = max(vN[0][i] + vN[1][j] + vN[2][k] + vN[3][l], ans)

print(ans)

# -*- coding: utf-8 -*-



"""

参考：https://atcoder.jp/contests/abc060/submissions/3958560

・前から普通にDPもdictでやってみる。

"""



import sys

def input(): return sys.stdin.readline().strip()

sys.setrecursionlimit(10 ** 9)



from collections import defaultdict



N, W = list(map(int, input().split()))

wN = [0] * N

vN = [0] * N

for i in range(N):

    wN[i], vN[i] = list(map(int, input().split()))



# 重さの添字にはdictを使う

dp = [defaultdict(int) for i in range(N+1)]

dp[0][0] = 0



for i in range(N):

    for k, v in list(dp[i].items()):

        # 重さの条件がOKなら、今回の値を足したケースを次のステップに送る

        if k + wN[i] <= W:

            dp[i+1][k+wN[i]] = max(dp[i][k] + vN[i], dp[i+1][k+wN[i]])

        # 足さないケースを送る

        dp[i+1][k] = max(dp[i][k], dp[i+1][k])



# 最適が重さWを全て使った場合とは限らないのでNステップ目のmaxを取る

print((max(dp[N].values())))

# -*- coding: utf-8 -*-
"""
参考：https://img.atcoder.jp/arc073/editorial.pdf
　　　https://ei1333.hateblo.jp/entry/2017/05/01/004235
　　　https://atcoder.jp/contests/abc060/submissions/3782609
・公式解
・累積和あり
"""
import sys


def input():
    return sys.stdin.readline().strip()


sys.setrecursionlimit(10**9)
from collections import defaultdict

N, W = list(map(int, input().split()))
wN = [0] * N
vN = [[] for i in range(4)]
for i in range(N):
    w, v = list(map(int, input().split()))
    if i == 0:
        offset = w
    vN[w - offset].append(v)
# 重さ毎のリストを降順ソート
for i in range(4):
    # 空リストのままだとこの後ループを回ってくれないので、[0]を追加する
    vN[i] = [0] + sorted(vN[i], reverse=True)
# 累積和
for i in range(4):
    for j in range(1, len(vN[i])):
        vN[i][j] = vN[i][j - 1] + vN[i][j]
ans = 0
for i in range(len(vN[0])):
    for j in range(len(vN[1])):
        for k in range(len(vN[2])):
            for l in range(len(vN[3])):
                # 重さリミットを超えるパターンは対象外
                if (
                    offset * i + (offset + 1) * j + (offset + 2) * k + (offset + 3) * l
                    > W
                ):
                    continue
                ans = max(vN[0][i] + vN[1][j] + vN[2][k] + vN[3][l], ans)
print(ans)

# -*- coding: utf-8 -*-
"""
参考：https://atcoder.jp/contests/abc060/submissions/3958560
・前から普通にDPもdictでやってみる。
"""
import sys


def input():
    return sys.stdin.readline().strip()


sys.setrecursionlimit(10**9)
from collections import defaultdict

N, W = list(map(int, input().split()))
wN = [0] * N
vN = [0] * N
for i in range(N):
    wN[i], vN[i] = list(map(int, input().split()))
# 重さの添字にはdictを使う
dp = [defaultdict(int) for i in range(N + 1)]
dp[0][0] = 0
for i in range(N):
    for k, v in list(dp[i].items()):
        # 重さの条件がOKなら、今回の値を足したケースを次のステップに送る
        if k + wN[i] <= W:
            dp[i + 1][k + wN[i]] = max(dp[i][k] + vN[i], dp[i + 1][k + wN[i]])
        # 足さないケースを送る
        dp[i + 1][k] = max(dp[i][k], dp[i + 1][k])
# 最適が重さWを全て使った場合とは限らないのでNステップ目のmaxを取る
print((max(dp[N].values())))

-参考：https://img.atcoder.jp/arc073/editorial.pdf -　　　https://ei1333.hateblo.jp/entry/2017/05/01/004235 -　　　https://atcoder.jp/contests/abc060/submissions/3782609 -・公式解 -・累積和あり +参考：https://atcoder.jp/contests/abc060/submissions/3958560 +・前から普通にDPもdictでやってみる。 -vN = [[] for i in range(4)] +vN = [0] * N -    w, v = list(map(int, input().split())) -    if i == 0: -        offset = w -    vN[w - offset].append(v) -# 重さ毎のリストを降順ソート -for i in range(4): -    # 空リストのままだとこの後ループを回ってくれないので、[0]を追加する -    vN[i] = [0] + sorted(vN[i], reverse=True) -# 累積和 -for i in range(4): -    for j in range(1, len(vN[i])): -        vN[i][j] = vN[i][j - 1] + vN[i][j] -ans = 0 -for i in range(len(vN[0])): -    for j in range(len(vN[1])): -        for k in range(len(vN[2])): -            for l in range(len(vN[3])): -                # 重さリミットを超えるパターンは対象外 -                if ( -                    offset * i + (offset + 1) * j + (offset + 2) * k + (offset + 3) * l -                    > W -                ): -                    continue -                ans = max(vN[0][i] + vN[1][j] + vN[2][k] + vN[3][l], ans) -print(ans) +    wN[i], vN[i] = list(map(int, input().split())) +# 重さの添字にはdictを使う +dp = [defaultdict(int) for i in range(N + 1)] +dp[0][0] = 0 +for i in range(N): +    for k, v in list(dp[i].items()): +        # 重さの条件がOKなら、今回の値を足したケースを次のステップに送る +        if k + wN[i] <= W: +            dp[i + 1][k + wN[i]] = max(dp[i][k] + vN[i], dp[i + 1][k + wN[i]]) +        # 足さないケースを送る +        dp[i + 1][k] = max(dp[i][k], dp[i + 1][k]) +# 最適が重さWを全て使った場合とは限らないのでNステップ目のmaxを取る +print((max(dp[N].values())))