You have N items and a bag with a weight limit of W. Each item has a weight (w_i) and a value (v_i). You need to select items to put in the bag such that the total weight does not exceed W, while maximizing the total value of the selected items. Constraints: 1 ≤ N ≤ 100, 1 ≤ W ≤ 10^9, 1 ≤ w_i ≤ 10^9, 1 ≤ v_i ≤ 10^7. Input format: N W, followed by N lines of w_i and v_i. Output the maximum possible total value of the selected items.

# Buggy version:
from collections import Counter,defaultdict,deque
from heapq import heappop,heappush
from bisect import bisect_left,bisect_right 
import sys,math,itertools,fractions,pprint
sys.setrecursionlimit(10**8)
mod = 10**9+7
INF = float('inf')
def inp(): return int(sys.stdin.readline())
def inpl(): return list(map(int, sys.stdin.readline().split()))

n,W = inpl()
a = [inpl() for _ in range(n)]
gds = [[0] for _ in range(4)]
a.sort()
a.sort(key = lambda x:x[1], reverse = True)
# print(a)
mi = a[0][0]
for i,(w,v) in enumerate(a):
    ind = w - mi
    gds[ind].append(v)
we = [i+mi for i in range(4)]
cnt = [0] * 4
for i in range(4):
    cnt[i] = len(gds[i])
    tmp = itertools.accumulate(gds[i])
    gds[i] = list(tmp)
# print(gds)
it = itertools.product(range(cnt[0]),range(cnt[1]),range(cnt[2]),range(cnt[3]))
res = 0
for ww in it:
    if sum(we[i]*ww[i] for i in range(4)) > W: continue
    now = sum(gds[i][ww[i]] for i in range(4))
    res = max(now, res)
print(res)

# Fixed version:
from collections import Counter,defaultdict,deque
from heapq import heappop,heappush
from bisect import bisect_left,bisect_right 
import sys,math,itertools,fractions,pprint
sys.setrecursionlimit(10**8)
mod = 10**9+7
INF = float('inf')
def inp(): return int(sys.stdin.readline())
def inpl(): return list(map(int, sys.stdin.readline().split()))

n,W = inpl()
a = [inpl() for _ in range(n)]
gds = [[0] for _ in range(4)]
a.sort(key = lambda x:x[1], reverse = True)
a.sort(key = lambda x:x[0])
# print(a)
mi = a[0][0]
for i,(w,v) in enumerate(a):
    ind = w - mi
    gds[ind].append(v)
we = [i+mi for i in range(4)]
cnt = [0] * 4
for i in range(4):
    cnt[i] = len(gds[i])
    tmp = itertools.accumulate(gds[i])
    gds[i] = list(tmp)
# print(gds)
it = itertools.product(range(cnt[0]),range(cnt[1]),range(cnt[2]),range(cnt[3]))
res = 0
for ww in it:
    if sum(we[i]*ww[i] for i in range(4)) > W: continue
    now = sum(gds[i][ww[i]] for i in range(4))
    res = max(now, res)
print(res)

# Buggy version:
import collections
N, W = map(int, input().split())
wv = collections.defaultdict(list)
w = 0
for i in range(N):
    a, b = map(int, input().split())
    wv[a].append(b)
    if i == 0:
        w = a
s = [[0]*(len(wv[w+i])+1) for i in range(4)]

for i in range(4):
    wv[w + i].sort(reverse=True)
    for j, x in enumerate(wv[w + i]):
        s[i][j+1] = s[i][j] + x
ans = 0
L = len(wv[3])
for i in range(min(len(wv[w]), N)+1):
    tmp1 = s[0][i]
    weight1 = w * i
    if weight1 > W:
        break
    for j in range(min(len(wv[w+1])+1, N+1 - i)):

        tmp2 = tmp1 + s[1][j]
        weight2 = weight1 + (w+1)*j
        if weight2 > W:
            break
        for k in range(min(len(wv[w+2])+1, N+1 - i - j)):
            tmp3 = tmp2 + s[2][k]
            weight3 = weight2 + (w+2)*k
            if weight3 > W:
                break
            d = W - weight3

            ans = max(ans, tmp3 + s[3][min(L, d//(w+3))])
print(ans)


# Fixed version:
import collections
N, W = map(int, input().split())
wv = collections.defaultdict(list)
w = 0
for i in range(N):
    a, b = map(int, input().split())
    wv[a].append(b)
    if i == 0:
        w = a
s = [[0]*(len(wv[w+i])+1) for i in range(4)]

for i in range(4):
    wv[w + i].sort(reverse=True)
    for j, x in enumerate(wv[w + i]):
        s[i][j+1] = s[i][j] + x
ans = 0
L = len(wv[w+3])
for i in range(min(len(wv[w]), N)+1):
    tmp1 = s[0][i]
    weight1 = w * i
    if weight1 > W:
        break
    for j in range(min(len(wv[w+1])+1, N+1 - i)):

        tmp2 = tmp1 + s[1][j]
        weight2 = weight1 + (w+1)*j
        if weight2 > W:
            break
        for k in range(min(len(wv[w+2])+1, N+1 - i - j)):
            tmp3 = tmp2 + s[2][k]
            weight3 = weight2 + (w+2)*k
            if weight3 > W:
                break
            d = W - weight3

            ans = max(ans, tmp3 + s[3][min(L, d//(w+3))])
print(ans)

# Buggy version:
N,W = map(int, input().split())
WV = [[int(i) for i in input().split()] for _ in range(N)]

MINW = WV[0][0]

W0,W1,W2,W3 = [],[],[],[]
for w,v in WV:
    if w == MINW: W0.append(v)
    elif w == MINW + 1: W1.append(v)
    elif w == MINW + 2: W2.append(v)
    elif w == MINW + 3: W3.append(v)

W0.sort(reverse=True)
W1.sort(reverse=True)
W2.sort(reverse=True)
W3.sort(reverse=True)

W0 = [0] + W0
W0 = [0] + W1
W0 = [0] + W2
W0 = [0] + W3

from itertools import accumulate

W0 = list(accumulate(W0))
W1 = list(accumulate(W1))
W2 = list(accumulate(W2))
W3 = list(accumulate(W3))

ans = 0
for cnt0 in range(len(W0)):
    for cnt1 in range(len(W1)):
        for cnt2 in range(len(W2)):
            for cnt3 in range(len(W3)):
                SUMW = cnt0 * MINW
                SUMW += cnt1 * (MINW + 1)
                SUMW += cnt2 * (MINW + 2)
                SUMW += cnt3 * (MINW + 3)

                if SUMW <= W:
                    SUMV = W0[cnt0]
                    SUMV += W1[cnt1]
                    SUMV += W2[cnt2]
                    SUMV += W3[cnt3]
                    ans = max(ans, SUMV)

print(ans)

# Fixed version:
N,W = map(int, input().split())
WV = [[int(i) for i in input().split()] for _ in range(N)]

MINW = WV[0][0]

W0,W1,W2,W3 = [],[],[],[]
for w,v in WV:
    if w == MINW: W0.append(v)
    elif w == MINW + 1: W1.append(v)
    elif w == MINW + 2: W2.append(v)
    elif w == MINW + 3: W3.append(v)

W0.sort(reverse=True)
W1.sort(reverse=True)
W2.sort(reverse=True)
W3.sort(reverse=True)

W0 = [0] + W0
W1 = [0] + W1
W2 = [0] + W2
W3 = [0] + W3

from itertools import accumulate

W0 = list(accumulate(W0))
W1 = list(accumulate(W1))
W2 = list(accumulate(W2))
W3 = list(accumulate(W3))

ans = 0
for cnt0 in range(len(W0)):
    for cnt1 in range(len(W1)):
        for cnt2 in range(len(W2)):
            for cnt3 in range(len(W3)):
                SUMW = cnt0 * MINW
                SUMW += cnt1 * (MINW + 1)
                SUMW += cnt2 * (MINW + 2)
                SUMW += cnt3 * (MINW + 3)

                if SUMW <= W:
                    SUMV = W0[cnt0]
                    SUMV += W1[cnt1]
                    SUMV += W2[cnt2]
                    SUMV += W3[cnt3]
                    ans = max(ans, SUMV)

print(ans)

# Buggy version:
def main():
    N, W = (int(i) for i in input().split())
    WV = [[int(i) for i in input().split()] for j in range(N)]
    for i in range(1, N):
        WV[i][0] -= WV[0][0]
    w_base = WV[0][0]
    WV[0][0] = 0
    V = {i: [] for i in range(4)}
    for w, v in WV:
        V[w].append(v)
    for i in range(4):
        V[i].sort(reverse=True)
    ans = 0
    from itertools import product
    for p in product(range(len(V[0])), range(len(V[1])),
                     range(len(V[2])), range(len(V[3]))):
        w_cur = w_base*p[0] + (w_base+1)*p[1] + \
            (w_base+2)*p[2] + (w_base+3)*p[3]
        if W < w_cur:
            continue
        ans = max(ans, sum(sum(V[i][:min(p[i], len(V[i]))]) for i in range(4)))
    print(ans)


if __name__ == '__main__':
    main()


# Fixed version:
def main():
    N, W = (int(i) for i in input().split())
    WV = [[int(i) for i in input().split()] for j in range(N)]
    for i in range(1, N):
        WV[i][0] -= WV[0][0]
    w_base = WV[0][0]
    WV[0][0] = 0
    V = {i: [0] for i in range(4)}
    for w, v in WV:
        V[w].append(v)
    for i in range(4):
        V[i].sort(reverse=True)
    ans = 0
    from itertools import product
    for p in product(range(len(V[0])), range(len(V[1])),
                     range(len(V[2])), range(len(V[3]))):
        w_cur = w_base*p[0] + (w_base+1)*p[1] + \
            (w_base+2)*p[2] + (w_base+3)*p[3]
        if W < w_cur:
            continue
        ans = max(ans, sum(sum(V[i][:min(p[i], len(V[i]))]) for i in range(4)))
    print(ans)


if __name__ == '__main__':
    main()

# Buggy version:
from itertools import accumulate
N,W = map(int,input().split())
w = [[] for i in range(4)]
w1,v1 = map(int,input().split())
w[0].append(v1)
for i in range(N-1):
    wi,vi = map(int,input().split())
    w[wi-w1].append(vi)
num = []
for i in range(4):
    w[i].sort(reverse= 1)
    w[i] = [0]+list(accumulate(w[i]))
    num.append(len(w[i]))
ans = 0
for n1 in range(num[0]+1):
    for n2 in range(num[1]+1):
        for n3 in range(num[2]+1):
            for n4 in range(num[3]+1):
                wc = w1*(n1+n2+n3+n4) + n2 + 2*n3 + 3*n4
                if wc <= W:
                    vc = w[0][n1]+w[1][n2]+w[2][n3]+w[3][n4]
                    ans = max(ans,vc)
print(ans)

# Fixed version:
from itertools import accumulate
N,W = map(int,input().split())
w = [[] for i in range(4)]
w1,v1 = map(int,input().split())
w[0].append(v1)
for i in range(N-1):
    wi,vi = map(int,input().split())
    w[wi-w1].append(vi)
num = []
for i in range(4):
    w[i].sort(reverse= 1)
    w[i] = [0]+list(accumulate(w[i]))
    num.append(len(w[i]))
ans = 0
for n1 in range(num[0]):
    for n2 in range(num[1]):
        for n3 in range(num[2]):
            for n4 in range(num[3]):
                wc = w1*(n1+n2+n3+n4) + n2 + 2*n3 + 3*n4
                if wc <= W:
                    vc = w[0][n1]+w[1][n2]+w[2][n3]+w[3][n4]
                    ans = max(ans,vc)
print(ans)

# Buggy version:
import sys
import math
from collections import defaultdict

sys.setrecursionlimit(10**7)
def input():
    return sys.stdin.readline()[:-1]

mod = 10**9 + 7

def I(): return int(input())
def LI(): return list(map(int, input().split()))
def LIR(row,col):
    if row <= 0:
        return [[] for _ in range(col)]
    elif col == 1:
        return [I() for _ in range(row)]
    else:
        read_all = [LI() for _ in range(row)]
        return map(list, zip(*read_all))

#################

from bisect import bisect_right

N,W = LI()
w,v = LIR(N,2)

w_list = []
for i in range(N+1):
    for j in range(i*w[0],i*(w[0]+3)+1):
        w_list.append(j)
w_list = sorted(list(set(w_list)))
M = len(w_list)

dw = defaultdict(int)
dwi = defaultdict(int)
temp = 0
for wl in w_list:
    dw[temp] = wl
    dwi[wl] = temp
    temp += 1

dp = [[0]*M for _ in range(N)]
for i in range(1,N):
    for j in range(M):
        dp[i][j] = dp[i-1][j]
        x = dw[j]-w[i]
        if x >= 0:
            dp[i][j] = max(dp[i][j], dp[i-1][dwi[x]]+v[i])

p = bisect_right(w_list,W)
print(dp[N-1][p-1])

# Fixed version:
import sys
import math
from collections import defaultdict

sys.setrecursionlimit(10**7)
def input():
    return sys.stdin.readline()[:-1]

mod = 10**9 + 7

def I(): return int(input())
def LI(): return list(map(int, input().split()))
def LIR(row,col):
    if row <= 0:
        return [[] for _ in range(col)]
    elif col == 1:
        return [I() for _ in range(row)]
    else:
        read_all = [LI() for _ in range(row)]
        return map(list, zip(*read_all))

#################

from bisect import bisect_right

N,W = LI()
w,v = LIR(N,2)

w_list = []
for i in range(N+1):
    for j in range(i*w[0],i*(w[0]+3)+1):
        w_list.append(j)
w_list = sorted(list(set(w_list)))
M = len(w_list)

d = {}
temp = 0
for wl in w_list:
    d[temp] = wl
    temp += 1

dp = [[0]*M for _ in range(N)]
for i in range(N):
    for j in range(M):
        dp[i][j] = dp[i-1][j]
        x = d[j]-w[i]
        if x >= 0:
            p = bisect_right(w_list,x)
            dp[i][j] = max(dp[i][j], dp[i-1][p-1]+v[i])

p = bisect_right(w_list,W)
print(dp[N-1][p-1])

# Buggy version:
# ARC073D - Simple Knapsack (ABC060D)
from itertools import accumulate


def main():
    N, W, *A = map(int, open(0).read().split())
    V, x = [[] for _ in range(4)], A[0]  # x: w1 (W1 ≤ wi ≤ w1 + 3)
    for i in range(0, 2 * N, 2):
        w, v = A[i : i + 2]
        V[w - x] += [v]
    for i in range(4):
        V[i] = tuple(accumulate([0] + sorted(V[i], reverse=1)))
    L = [len(v) for v in V]
    print(V, L)
    ans = 0
    for i in range(L[0]):
        for j in range(L[1]):
            for k in range(L[2]):
                for l in range(L[3]):
                    w = i * x + j * (x + 1) + k * (x + 2) + l * (x + 3)
                    if w > W:
                        break
                    v = V[0][i] + V[1][j] + V[2][k] + V[3][l]
                    ans = max(ans, v)
    print(ans)


if __name__ == "__main__":
    main()

# Fixed version:
# ARC073D - Simple Knapsack (ABC060D)
from itertools import accumulate


def main():
    N, W, *A = map(int, open(0).read().split())
    V, x = [[] for _ in range(4)], A[0]  # x: w1 (W1 ≤ wi ≤ w1 + 3)
    for i in range(0, 2 * N, 2):
        w, v = A[i : i + 2]
        V[w - x] += [v]
    for i in range(4):
        V[i] = tuple(accumulate([0] + sorted(V[i], reverse=1)))
    L = [len(v) for v in V]
    ans = 0
    for i in range(L[0]):
        for j in range(L[1]):
            for k in range(L[2]):
                w = i * x + j * (x + 1) + k * (x + 2)
                if w > W:
                    break
                l = min(L[3] - 1, (W - w) // (x + 3))
                v = V[0][i] + V[1][j] + V[2][k] + V[3][l]
                ans = max(ans, v)
    print(ans)


if __name__ == "__main__":
    main()

# Buggy version:
from itertools import accumulate
N, W = map(int, input().split())
w_0, v_0 = map(int, input().split())
v_list = [[] for _ in range(4)]
v_list[0].append(v_0)

for _ in range(n - 1):
    w_in, v_in = map(int, input().split())
    v_list[w_in - w_0].append(v_in)
v_list = [[0] + list(accumulate(sorted(v, reverse=True))) for v in v_list]
ans = 0

#print(v_list)

for i in range(len(v_list[0])):
    for j in range(len(v_list[1])):
        for k in range(len(v_list[2])):
            for l in range(len(v_list[3])):
                if (i * w_0) + (j * (w_0 + 1)) + (k * (w_0 + 2)) + (l * (w_0 + 3)) <= W:
                    ans = max(ans, v_list[0][i] + v_list[1][j] + v_list[2][k] + v_list[3][l])
                else:
                    break
print(ans)



# Fixed version:
from itertools import accumulate
n, w = map(int, input().split())
w_0, v_0 = map(int, input().split())
v_list = [[] for _ in range(4)]
v_list[0].append(v_0)
for _ in range(n - 1):
    w_in, v_in = map(int, input().split())
    v_list[w_in - w_0].append(v_in)
v_list = [[0] + list(accumulate(sorted(v, reverse=True))) for v in v_list]
ans = 0
for i in range(len(v_list[0])):
    for j in range(len(v_list[1])):
        for k in range(len(v_list[2])):
            for l in range(len(v_list[3])):
                if ((i) * w_0) + ((j) * (w_0 + 1)) + ((k) * (w_0 + 2)) + (((l) * (w_0 + 3))) <= w:
                    ans = max(ans, v_list[0][i] + v_list[1][j] + v_list[2][k] + v_list[3][l])
                else:
                    break
print(ans)

# Buggy version:
n, w = map(int, input().split())
di = dict()
m = 0
for _ in range(n):
  x,y = map(int, input().split())
  if x not in di:
    di[x] = []
  di[x].append(y)
for i in di:
  di[i].sort(reverse=True)
a,b,c,d = di.keys()
for i in range(len(di[a])):
  for j in range(len(di[b])):
    for k in range(len(di[c])):
      for l in range(len(di[d])):
        if i*a + j*b + k*c + l*d <= w:
          m = max(m, sum(di[a][:i+1] + di[b][:j+1] + di[c][:k+1] + di[d][:l+1]))
print(m)

# Fixed version:
n, w = map(int, input().split())
di = dict()
m = 0
for _ in range(n):
  x,y = map(int, input().split())
  if x not in di:
    di[x] = []
  di[x].append(y)
for i in di:
  di[i].sort(reverse=True)
a = min(di.keys())
b,c,d = a+1,a+2,a+3
for i in b,c,d:
  if i not in di:
    di[i] = []
for i in range(len(di[a])+1):
  for j in range(len(di[b])+1):
    for k in range(len(di[c])+1):
      for l in range(len(di[d])+1):
        if i*a + j*b + k*c + l*d <= w:
          m = max(m, sum(di[a][:i] + di[b][:j] + di[c][:k] + di[d][:l]))
print(m)

# Buggy version:
n,w = map(int, input().split())
w4 = [[] for i in range(4)]
w1 = None
for i in range(n):
    ws, vs = map(int,input().split())
    if i == 0:
        w1 = ws
    ws -= w1
    w4[ws].append(vs)

for i in range(4):
    w4[i].sort(reverse=True)
    tmp = [0]
    for j in w4[i]:
        tmp.append(tmp[-1]+j)
    w4[i] = tmp
ans = 0
for i in range(len(w4[0])):
    for j in range(len(w4[1])):
        for k in range(len(w4[2])):
            for l in range(len(w4[3])):
                if w >= w1*(i+j+k+l) + j + 2*k + 3*l:
                    print(i,j,k,l)
                    ans = max(ans, w4[0][i]+w4[1][j]+w4[2][k]+w4[3][l])
print(ans)

# Fixed version:
n,w = map(int, input().split())
w4 = [[] for i in range(4)]
w1 = None
for i in range(n):
    ws, vs = map(int,input().split())
    if i == 0:
        w1 = ws
    ws -= w1
    w4[ws].append(vs)

for i in range(4):
    w4[i].sort(reverse=True)
    tmp = [0]
    for j in w4[i]:
        tmp.append(tmp[-1]+j)
    w4[i] = tmp
ans = 0
for i in range(len(w4[0])):
    for j in range(len(w4[1])):
        for k in range(len(w4[2])):
            for l in range(len(w4[3])):
                if w >= w1*(i+j+k+l) + j + 2*k + 3*l:
                    ans = max(ans, w4[0][i]+w4[1][j]+w4[2][k]+w4[3][l])
print(ans)

# Buggy version:
from itertools import accumulate
import heapq
# python template for atcoder1
import sys
sys.setrecursionlimit(10**9)
input = sys.stdin.readline

N, W = map(int, input().split())
first_item = list(map(int, input().split()))
basew = first_item[0]
w0 = []
w1 = []
w2 = []
w3 = []
heapq.heappush(w0, first_item[1])
for _ in range(N-1):
    w, v = map(int, input().split())
    w -= basew
    if w == 0:
        heapq.heappush(w0, v)
    elif w == 1:
        heapq.heappush(w1, v)
    elif w == 2:
        heapq.heappush(w2, v)
    elif w == 3:
        heapq.heappush(w3, v)

w0_acc = [0]+list(accumulate(reversed(w0)))
w1_acc = [0]+list(accumulate(reversed(w1)))
w2_acc = [0]+list(accumulate(reversed(w2)))
w3_acc = [0]+list(accumulate(reversed(w3)))
v = 0
for i0 in range(len(w0)):
    for i1 in range(len(w1)):
        for i2 in range(len(w2)):
            for i3 in range(len(w3)):
                if basew*(i0+i1+i2+i3)+3*i3 + 2*i2 + i1 > W:
                    break
                else:
                    v = max(v, w0_acc[i0]+w1_acc[i1]+w2_acc[i2]+w3_acc[i3])
print(v)


# Fixed version:
from itertools import accumulate
import heapq
# python template for atcoder1
import sys
sys.setrecursionlimit(10**9)
input = sys.stdin.readline

N, W = map(int, input().split())
first_item = list(map(int, input().split()))
basew = first_item[0]
w0 = []
w1 = []
w2 = []
w3 = []
w0.append(first_item[1])
for _ in range(N-1):
    w, v = map(int, input().split())
    w -= basew
    if w == 0:
        w0.append(v)
    elif w == 1:
        w1.append(v)
    elif w == 2:
        w2.append(v)
    elif w == 3:
        w3.append(v)

w0_acc = [0]+list(accumulate(sorted(w0, reverse=True)))
w1_acc = [0]+list(accumulate(sorted(w1, reverse=True)))
w2_acc = [0]+list(accumulate(sorted(w2, reverse=True)))
w3_acc = [0]+list(accumulate(sorted(w3, reverse=True)))
v = 0
for i0 in range(len(w0)+1):
    for i1 in range(len(w1)+1):
        for i2 in range(len(w2)+1):
            for i3 in range(len(w3)+1):
                if basew*(i0+i1+i2+i3)+3*i3 + 2*i2 + i1 > W:
                    break
                else:
                    v = max(v, w0_acc[i0]+w1_acc[i1]+w2_acc[i2]+w3_acc[i3])
print(v)

# Buggy version:
N, W = (int(i) for i in input().split())
w = [0]*N
w0 = w[0]
wh = [a - w0 for a in W]
v = [0]*N
for i in range(N):
  w[i], v[i] = (int(i) for i in input().split())
dp = [[[0]*(N+1) for i in range(3N+1)] for j in range(N+1)]
for i in range(N):
  for j in range(3N+1):
    for k in range(N+1):
      dp[i+1][j][k] = max(dp[i+1][j][k], dp[i][j][k])
      if j - wh[i] >=0 and k-1 >=0:
        dp[i+1][j][k] = max(dp[i+1][j][k], dp[i][j-wh[i]][k-1]+v[i])
res = 0
for k in range(N+1):
  if w0*k <= W <= (w0+3)*k:
    Wk = W - w0*k
    ans = 0
    for j in range(Wk+1):
      ans = max(dp[N][j][k], ans)
    res = max(res, ans)
print(res)

# Fixed version:
N, W = (int(i) for i in input().split())
w = [0]*N
v = [0]*N
for i in range(N):
  w[i], v[i] = (int(j) for j in input().split())
w0 = w[0]
wh = [a - w0 for a in w]
dp = [[[-1]*(N+1) for i in range(3*N+1)] for j in range(N+1)]
dp[0][0][0] = 0
for i in range(N):
  for j in range(3*N+1):
    for k in range(N+1):
      if dp[i][j][k] != -1:
        dp[i+1][j][k] = max(dp[i+1][j][k], dp[i][j][k])
      if j - wh[i] >=0 and k-1 >=0 and dp[i][j-wh[i]][k-1] != -1:
        dp[i+1][j][k] = max(dp[i+1][j][k], dp[i][j-wh[i]][k-1]+v[i])
res = 0
for j in range(3*N+1):
  for k in range(N+1):
    if j + w0*k <= W:
      res = max(dp[N][j][k], res)
print(res)

# Buggy version:
ints = input().split(" ")
N = int(ints[0])
maxW = int(ints[1])
ints = input().split(" ")
a = int(ints[0])
b = int(ints[1])
weight = [a, a+1, a+2, a+3] 
va = [[0,b],[0],[0],[0]]
for i in range(1, N):
	ins = input().split(" ")
	w = int(ins[0])
	v = int(ins[1])
	for j in range(0, 4):
		if w == weight[j]:
			va[j].append(v)
va.sort()
sum = [[0],[0],[0],[0]]
for j in range(0, 4):
	for i in range(1, len(va[j])):
		try:
			sum[j].append(sum[j][i-1] + va[j][i])
		except:
			pass
ans = 0
for i in range(0, len(va[0])):
	for j in range(0, len(va[1])):
		for k in range(0, len(va[2])):
			for l in range(0, len(va[3])):
				sumw = weight[0]*i + weight[1]*j +weight[2]*k + weight[3]*l
				if(sumw <= maxW):
					ans = max(sum[0][i]+sum[1][j]+sum[2][k]+sum[3][l],ans)
print(ans)

# Fixed version:
ints = input().split(" ")
N = int(ints[0])
maxW = int(ints[1])
ints = input().split(" ")
a = int(ints[0])
b = int(ints[1])
weight = [a, a+1, a+2, a+3] 
va = [[b],[],[],[]]
for i in range(1, N):
	ins = input().split(" ")
	w = int(ins[0])
	v = int(ins[1])
	for j in range(0, 4):
		if w == weight[j]:
			va[j].append(v)
for i in range(0, len(va)):
	va[i].sort(reverse=True)
	va[i].insert(0,0)
#print(va)
sum = [[0],[0],[0],[0]]
for j in range(0, 4):
	for i in range(1, len(va[j])):
		try:
			sum[j].append(sum[j][i-1] + va[j][i])
		except:
			pass
ans = 0
#print(sum)
for i in range(0, len(va[0])):
	for j in range(0, len(va[1])):
		for k in range(0, len(va[2])):
			for l in range(0, len(va[3])):
				sumw = weight[0]*i + weight[1]*j +weight[2]*k + weight[3]*l
				if(sumw <= maxW):
					ans = max(sum[0][i]+sum[1][j]+sum[2][k]+sum[3][l],ans)
print(ans)