jiahuimbzuai/CoQuIR huggingface dataset:
//  [java]
import java.util.Scanner;

public class Main {
	public static void main(String[]args) {
		try(Scanner scan = new Scanner(System.in)){

			int N = scan.nextInt();
			int W = scan.nextInt();
			int[]w = new int[N];
			int[]v = new int[N];
			for(int i = 0;i<N;i++) {
				w[i] = scan.nextInt();
				v[i] = scan.nextInt();
			}

			long[][]dp = new long[N+1][W+1];


			for(int z = 0;z<=W;z++) {
				for(int i = 1;i<=N;i++) {
					if(z-w[i]>=0) {
						dp[i][z] = Math.max(dp[i-1][z-w[i]]+v[i], dp[i-1][z]);
					}else {
						dp[i][z] = dp[i-1][z];
				}
			}
			}

			/*
			for(int i = 0;i<=W;i++) {
				for(int j = 0;j<N;j++) {
					System.out.print(dp[j][i]+" ");
				}
				System.out.println();
			}
			*/


			System.out.println(dp[N][W]);


		}


	}


}
//  [java]
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;

public class Main {

    public static void main(String[] args) throws Exception {

        ModScanner ms = new ModScanner();
        OutputStream outputStream = System.out;
        PrintWriter out = new PrintWriter(outputStream);
        int n = ms.nextInt();
        int w = ms.nextInt();
        int[] wt = new int[n];
        int[] val = new int[n];
        for (int i = 0; i < n; i++) {
            wt[i] = ms.nextInt();
            val[i] = ms.nextInt();
        }

        out.println(maxProfit(wt, val, w, 0, new HashMap<>()));
        out.close();
    }

    private static long maxProfit(int[] wt, int[] val, int w, int ind, Map<String, Long> cache) {
        if (w < 0) {
            return Long.MIN_VALUE;
        }
        if (w == 0 || ind == wt.length) {
            return 0;
        }
        String key = ind + "|" + w;
        if (!cache.containsKey(key)) {
            long include = maxProfit(wt, val, w - wt[ind], ind + 1, cache) + val[ind];
            long exclude = maxProfit(wt, val, w, ind + 1, cache);
            cache.put(key, Math.max(include, exclude));
        }
        return cache.get(key);
    }

}

class ModScanner {

    BufferedReader br;
    StringTokenizer st;

    public ModScanner() {
        br = new BufferedReader(new InputStreamReader(System.in));
    }

    String nextToken() throws Exception {
        while (st == null || !st.hasMoreElements()) {
            st = new StringTokenizer(br.readLine());

        }
        return st.nextToken();
    }

    String nextString() throws Exception {
        return br.readLine();
    }


    int nextInt() throws Exception, Exception {
        return Integer.parseInt(nextToken());
    }

    long nextLong() throws Exception {
        return Long.parseLong(nextToken());
    }

    double nextDouble() throws Exception {
        return Double.parseDouble(nextToken());
    }

}
//  [java]
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;

public class Main {

    public static void main(String[] args) throws Exception {

        ModScanner ms = new ModScanner();
        OutputStream outputStream = System.out;
        PrintWriter out = new PrintWriter(outputStream);
        int n = ms.nextInt();
        int w = ms.nextInt();
        int[] wt = new int[n];
        int[] val = new int[n];
        for (int i = 0; i < n; i++) {
            wt[i] = ms.nextInt();
            val[i] = ms.nextInt();
        }

        out.println(maxProfit(wt, val, w, 0, new HashMap<>()));
        out.close();
    }

    private static long maxProfit(int[] wt, int[] val, int w, int ind, Map<String, Long> cache) {
        if (w < 0) {
            return Long.MIN_VALUE;
        }
        if (w == 0 || ind == wt.length) {
            return 0;
        }
        String key = ind + "#" + w;
        if (!cache.containsKey(key)) {
            long include = maxProfit(wt, val, w - wt[ind], ind + 1, cache) + val[ind];
            long exclude = maxProfit(wt, val, w, ind + 1, cache);
            cache.put(key, Math.max(include, exclude));
        }
        return cache.get(key);
    }

}

class ModScanner {

    BufferedReader br;
    StringTokenizer st;

    public ModScanner() {
        br = new BufferedReader(new InputStreamReader(System.in));
    }

    String nextToken() throws Exception {
        while (st == null || !st.hasMoreElements()) {
            st = new StringTokenizer(br.readLine());

        }
        return st.nextToken();
    }

    String nextString() throws Exception {
        return br.readLine();
    }


    int nextInt() throws Exception, Exception {
        return Integer.parseInt(nextToken());
    }

    long nextLong() throws Exception {
        return Long.parseLong(nextToken());
    }

    double nextDouble() throws Exception {
        return Double.parseDouble(nextToken());
    }

}
//  [cpp]
//#include <bits/stdc++.h>/*
#include <unordered_map>
#include <functional> //greater
#include <algorithm> //sort
#include <iostream>
#include <iterator>
#include <utility> //pair
#include <sstream>
#include <cstring> //memset
#include <complex>
#include <cassert>
#include <random>
#include <cstdio>
#include <string>
#include <vector>
#include <cctype> //isaplha, tolower
#include <deque>
#include <queue>
#include <stack>
#include <array>
#include <cmath> //sqrt, sin
#include <ctime>
#include <list>
#include <map>
#include <set>
//*/
 
#define int long long
#define uns unsigned
#define ll long long
#define vi vector<int>
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define endl '\n'
using namespace std;
 
template<class T> ostream &operator<<(ostream &os, const pair<T,T> &p){
	return os<<"("<<p.fi<<","<<p.se<<")";}
template<class T> ostream &operator<<(ostream &os, const pair<const T,T> &p){
	return os<<"("<<p.fi<<","<<p.se<<")";}
template<class T> ostream &operator<<(ostream &os, const array<T,3> &p){
	return os<<"("<<p[0]<<","<<p[1]<<","<<p[2]<<")";}
template<class T> ostream &operator<<(ostream &os, const vector<T> &p){
	for(auto x: p) os<<x<<' '; return os;}
#define dbug(x) cerr<<">>>> "<<#x<<" = "<<x<<endl
#define _ <<", "<<
#define FOR(X,L,R) for(int X=L;X<R;X++)
#define FI(I,X) for(auto I=(X).begin();I!=(X).end();I++)
#define print(X) {cerr<<"{ ";FI(I,X)cerr<<*I<<' ';cerr<<"}"<<endl;}
#define mset(V,X) memset(V,X,sizeof(V))
#define all(X) (X).begin(),(X).end()
#define upperb(V,X) (int)(upper_bound(all(V),(X))-V.begin())
#define lowerb(V,X) (int)(lower_bound(all(V),(X))-V.begin())
 
const double EPS = 1e-9, PI = acos(-1);
const ll LINF = 0x3f3f3f3f3f3f3f3f, mod = 1e9+7;
const int INF = 0x3f3f3f3f, SZ = 2e5+20;
 
int n,c;
vector<pii> v;
int res,aux;
double c2,aux2;
void bt(int i){
	if(i == n) return;
 
	aux2 = 0; c2 = c;
	for(int j=i; j<n && c2; j++){
		if(v[j].fi <= c2){
			c2 -= v[j].fi; aux2 += v[j].se;
		} else {
			aux2 += (v[j].se*c2)/v[j].fi;
			c2 = 0;
		}
	}
	if(aux2 + aux <= res) return;
  
	if(v[i].fi <= c){
		c -= v[i].fi;
		aux += v[i].se;
		if(aux > res) res = aux;
		bt(i+1);
		aux -= v[i].se;
		c += v[i].fi;
	}
	bt(i+1);
}
 
int32_t main(){
	ios::sync_with_stdio(false);cin.tie(0);
	
	cin>>n>>c;
	FOR(i,0,n){
		int x,y; cin>>x>>y;
		v.pb({x,y});
	}
 
	sort(all(v),[](pii a, pii b){
		return (a.se+0.0)/a.fi > (b.se+0.0)/b.fi;
	});
	
	bt(0);
	cout<<res<<endl;
 
	return 0;
}
//  [cpp]
#include <algorithm>
#include <iostream>
#include <vector>
#define REP(i, n) for (int i = 0; i < (int)(n); ++i)
using namespace std;
typedef long long ll;

void iostream_init() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.setf(ios::fixed);
    cout.precision(12);
}

class Knapsack {
    struct item {
        ll v, w;
    };

    vector<item> items;
    ll W, lv;

    ll solve_rec(size_t k, ll v, ll w) {
        if (w + items[k].w > W)
            return solve_rec(k + 1, v, w);
        ll cv = v, cw = w;
        for (size_t i = k; i < items.size(); ++i) {
            if (cw + items[i].w <= W) {
                cw += items[i].w;
                cv += items[i].v;
            }
        }
        if (lv < cv)
            lv = cv;
        double fv = v, fw = w;
        for (size_t i = k; i < items.size(); ++i) {
            if (fw + items[i].w <= W) {
                fw += items[i].w;
                fv += items[i].v;
            } else {
                fv += items[i].v * (W - fw) / items[i].w;
                break;
            }
        }
        if (fv - lv < 1 || fv < lv)
            return lv;
        solve_rec(k + 1, v + items[k].v, w + items[k].w);
        return solve_rec(k + 1, v, w);
    }

  public:
    // 価値v, 重みwのアイテムを追加
    void add_item(ll v, ll w) { items.push_back({v, w}); }

    // 重みの合計がW以下になるようにアイテムを選んだときの価値の和の最大値を返す．
    ll solve(ll W_) {
        if (items.empty())
            return 0;
        W = W_;
        sort(items.begin(), items.end(), [](const item &a, const item &b) {
            return a.v * b.w > a.w * b.v;
        });
        lv = 0;
        return solve_rec(0, 0, 0);
    };
};

int main() {
    iostream_init();
    int N, W;
    cin >> N >> W;
    Knapsack dp;
    for (int i = 0; i < N; i++) {
        int w, v;
        cin >> w >> v;
        dp.add_item(v, w);
    }

    cout << dp.solve(W) << endl;
    return 0;
}
//  [cpp]
#include <bits/stdc++.h>
#define rep(i,n) for (int i = 0; i < (n); ++i)
#define rrep(i,n) for(int i = 1; i <= (n); ++i)
using namespace std;
using ll = long long;
using P = pair<int,int>;

ll dp[100005][100005];

const int mod = 1000000007;
int main() {
  int n, W;
  cin >> n >> W;
  vector<int> w(n), v(n);
  rep(i, n){
    cin >> w[i] >> v[i];
  }
  rep(i, n+1){
    rep(j, W+1){
      if(j < w[i]) dp[i+1][j] = dp[i][j];
      else dp[i+1][j] = max(dp[i][j], dp[i][j-w[i]] + v[i]);
    }
  }
  cout << dp[n][W] << endl;
  return 0;
}
//  [cpp]
#include<bits/stdc++.h>
using namespace std;
#define rep(i,n) for (int i=0; i<n; i++)

int main(){
    int n, w;
    cin >> n >> w;
    int W[110], V[110];
    rep(i,n) cin >> W[i] >> V[i];

    int dp[110][100010];
    rep(i,n){
        rep(j,100010){
            dp[i+1][j] = dp[i][j];
            if (j >= W[i]) dp[i+1][j] = max(dp[i][j-W[i]]+V[i], dp[i+1][j]);
        }
    }

    cout << dp[n][w] << endl;
    return 0;
}
//  [cpp]
#include<bits/stdc++.h>
using namespace std;
#define rep(i,n) for (int i=0; i<n; i++)

int main(){
    int n, w;
    cin >> n >> w;
    vector<int> W[110], V[110];
    rep(i,n) cin >> W[i] >> V[i];

    int dp[110][100010];
    rep(i,n){
        rep(j,100010){
            dp[i+1][j] = dp[i][j];
            if (j >= W[i]) dp[i+1][j] = max(dp[i][j-W[i]]+V[i], dp[i+1][j]);
        }
    }

    cout << dp[n][w] << endl;
    return 0;
}
//  [c]
#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
#include<inttypes.h>

typedef int32_t i32;
typedef int64_t i64;

#define MAX(a,b) ((a)>(b)?(a):(b))
#define ALLOC(size,type) ((type*)calloc((size),sizeof(type)))

void run (void) {
  i32 n, w;
  scanf ("%" SCNi32 "%" SCNi32, &n, &w);
  i64 *dp = ALLOC (w + 1, i64);
  while (n--) {
    i32 a, b;
    scanf ("%" SCNi32 "%" SCNi32, &a, &b);
    for (i32 i = w; i >= a; --i) {
      i64 x = dp[i - a] + b;
      dp[i] = MAX (dp[i], x);
    }
  }
  printf ("%" PRIi64 "\n", dp[w]);
}

int main (void) {
  run();
  return 0;
}
//  [c]
#include <stdio.h>
#include <stdlib.h>
#define int long long

int max(int a, int b){
	return a >= b ? a : b;
}

signed main(){
	int N, W, w, v, i, j;
	scanf("%lld%lld", &N, &W);
	int *dp = (int *)malloc(sizeof(int) * (W + 1));
	for(j = 0; j <= W; j++){
		dp[j] = 0;
	}
	for(i = 0; i < N; i++){
		scanf("%lld%lld", &w, &v);
		for(j = W; j >= w; j--){
			dp[j] = max(dp[j], dp[j - w] + v);
		}
	}
	printf("%lld\n", dp[W]);
	return 0;
}
//  [c]
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#define NOT_ZERO 1
#define MAX(a,b) (a>b?a:b)
#define MAX3(a,b,c) MAX(a,MAX(b,c))
#define MIN(a,b) (a<b?a:b)
#define MIN3(a,b,c) MIN(a, MIN(b,c))
typedef long long ll;

int main()
{
    int N, W;
    int w[100], v[100];
    int i, j;

    scanf("%d %d",&N,&W);

    ll *dp = (ll*)calloc(W+1, sizeof(ll));  //容量が最大iの時の価値の最大値

    for(i=0; i<N; i++) {
        scanf("%d %d",w+i, v+i);
        for(j=W; j>=w[i]; j--) {
            dp[j] = MAX(dp[j], dp[j-w[i]]+v[i]);
        }
    }
    printf("%lld",dp[W]);

    return 0;
}
//  [c]
#include <bits/stdc++.h>
using namespace std;
long long arr [110000];
 
int main ()
{
int n,w;
int wi,vi;
cin >>n>>w;
int i,j;
for (i = 1 ;i <= n;i++)
{
cin >>wi>>vi;
for (j = w - wi;j >= 0;j --)
{
arr [j + wi] = max (arr [j + wi] , arr [j] + vi);
}
}
long long t ;
t = 0;
for (i = 0 ; i <= w;i ++)
{
t = max (t , arr [i]);
}
cout <<t<<'\n';
 
}
//  [c]
#include <stdio.h>
#include <stdlib.h>

void    chmax(long long *left, long long right)
{
        if (*left < right)
        {
                *left = right;
        }
}

int             main(void)
{
        int                     n;
        int                     w_max;
        int                     weight[100];
        int                     value[100];
        long long       answer[100][10001] = {0};
        long long       max = 0;
        int                     i;
        int                     j;

        scanf("%d%d", &n, &w_max);
        for (i = 0; i < n; i++)
        {
                scanf("%d%d", &weight[i], &value[i]);
        }
        for (i = 0; i <= w_max; i++)
        {
                if (i < weight[0])
                        answer[0][i] = 0;
                else
                        answer[0][i] = value[0];
        }
        for (i = 1; i < n; i++)
        {
                for (j = 0; j <= w_max; j++)
                {
                        if (j + weight[i] <= w_max)
                        {
                                chmax(&answer[i][j + weight[i]], answer[i - 1][j] + value[i]);
                        }
                        chmax(&answer[i][j], answer[i - 1][j]);
                }
        }
        for (i = 0; i <= w_max; i++)
        {
                chmax(&max, answer[n - 1][i]);
        }
        printf("%lld\n", max);
        return (0);
}
//  [c]
#include <stdio.h>

int             N;
int             W_MAX;
int             WEIGHT[100];
int             VALUE[100];

void    culc_max(int weight, long long value, int i, long long *max)
{
        if (i == N)
        {
                if (weight <= W_MAX)
                {
                        if (*max < value)
                                *max = value;
                }
                return ;
        }
        culc_max(weight + WEIGHT[i], value + VALUE[i], i + 1, max);
        culc_max(weight, value, i + 1, max);
}

int             main(void)
{
        long long       max = 0;
        int                     i;

        scanf("%d%d", &N, &W_MAX);
        for (i = 0; i < N; i++)
        {
                scanf("%d%d", &WEIGHT[i], &VALUE[i]);
        }
        culc_max(0, 0, 0, &max);
        printf("%lld\n", max);
        return (0);
}
//  [javascript]
var readline = require('readline');

var knapSack = async function(){
  let n,capacity;

  let val = []
  let weight = [];

  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
    terminal: false
  });

  async function processLineByLine() {
    const rl1 = readline.createInterface({
      input: process.stdin,
      output: process.stdout,
      terminal: false
    });
    let index = 0;
    for await (const line of rl1) {
      let [one, two] = line.trim().split(" ").map(x => parseInt(x));
      if(!index){
        [n,capacity] = [one,two]
      } else {
        weight.push(one);
        val.push(two);
        // console.log(val);
      }
      index++;
      if(index === n+1){
        return;
      }
    }
  }

  await processLineByLine();

  let mem = [];

  for(let i = 0 ; i <= capacity ; i++){
    mem.push(0);
  }

  for(let i=1;i<= n ; i++){
    for(let j=capacity;j > 0;j--){
      let valWithoutItem = mem[j];
      let valWithItem = 0;
      if(weight[i-1] <= j){
        valWithItem = val[i-1] + mem[j - weight[i-1]];
      }
      mem[j] = Math.max(valWithItem, valWithoutItem);
    }
  }

  console.log(mem[capacity]);
};

knapSack();
//  [javascript]
"use strict";

function main(input){
  const args = input.trim().split('\n').map(_=>_.split(' ').map(_=>parseInt(_,10)));
  const n = args[0][0];
  const w = args[0][1];
  const items = args.slice(1);
  
  const dp = Array(w+1).fill(0); // 容量をi使うときの最大価値
  for(let i=0; i<n; i++){
    const weight = items[i][0];
    const value = items[i][1];
    for(let k=w; k>=weight; k--){
      dp[k] = Math.max(dp[k-weight] + value, dp[k]);
    }
  }
  
  console.log(dp.reduce((acc,cur)=>Math.max(acc,cur)));
}

main(require('fs').readFileSync('/dev/stdin', 'utf8'));
//  [javascript]
const W = [3, 4, 5];
const V = [30, 50, 60];

function knapsack(pos, curr_weight) {
  if (pos == W.length) {
    return 0;
  }

  if (curr_weight - W[pos] >= 0) {
    
    return Math.max(
      knapsack(pos + 1, curr_weight - W[pos]) + V[pos],
      knapsack(pos + 1, curr_weight)
    );
  } else {
    return knapsack(pos + 1, curr_weight);
  }
}

console.log(knapsack(0, 8));
//  [javascript]
'use strict'
const lines = require('fs').readFileSync(0, 'utf8').split('\n')
const firstLine = lines[0].split(' ').map(Number)
const N = firstLine[0]
const W = firstLine[1]
var dp = new Array(N + 1);
for(var i = 0; i < N + 1; i++){
  dp[i] = new Array(W + 1).fill(0);
}

for (let i=0;i < N;++i) {
  const vw = lines[i].split(' ')
  for (let w=0; w<=W; ++w) {
    if (w - vw[0] >= 0) {
      dp[i+1][w] = Math.max(dp[i+1][w], dp[i][w-vw[0]] + vw[1])
    }
    dp[i+1][w] = dp[i][w]
  }
}
console.log(dp[N][W])
//  [javascript]
'use strict'
const lines = require('fs').readFileSync(0, 'utf8').split('\n')
const firstLine = lines[0].split(' ').map(Number)
const N = firstLine[0]
const W = firstLine[1]
var dp = new Array(N + 1);
for(var i = 0; i < N + 1; i++){
  dp[i] = new Array(W + 1).fill(0);
}

for (let i=0;i < N;++i) {
  const vw = lines[i].split(' ')
  for (let w=0; w<=W; ++w) {
    if (w - vw[0] >= 0) {
      dp[i+1][w] = Math.max(dp[i+1][w], dp[i][w-vw[0]] + vw[1])
    }
    dp[i+1][w] = Math.max(dp[i+1][w], dp[i][w])
  }
}
console.log(dp[N][W])
//  [go]
package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
)

func main() {
	sc := NewScanner(os.Stdin)
	n, maxWeight := sc.Int(), sc.Int()

	weights, values := make([]int, n+1), make([]int, n+1)
	for i := 1; i <= n; i++ {
		weights[i] = sc.Int()
		values[i] = sc.Int()
	}

	m := make([]int, maxWeight+1)

	for i := range weights {
		w, v := weights[i], values[i]

		for j := maxWeight; j >= w; j-- {
			m[j] = Max(m[j], m[j-w]+v)
		}
	}

	fmt.Println(m[maxWeight])
}

// generated by https://github.com/murosan/gollect

func Max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

type Scanner struct{ *bufio.Scanner }

func NewScanner(r io.Reader) *Scanner {
	s := bufio.NewScanner(r)
	s.Split(bufio.ScanWords)
	return &Scanner{Scanner: s}
}

func (s *Scanner) Bytes() []byte { s.Scan(); return s.Scanner.Bytes() }

func (s *Scanner) Int() int { return BytesToInt(s.Bytes()) }

func BytesToInt(b []byte) (n int) {
	var i int
	negative := b[0] == 45
	if negative {
		i++
	}
	for ; i < len(b); i++ {
		n = n*10 + int(b[i]-'0')
	}
	if negative {
		return -n
	}
	return
}
//  [go]
package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"os"
	"strconv"
)

func main() {
	n, w := readInt(), readInt()
	dp := make([]int, w+1)

	for i := 0; i < n; i++ {
		ww, v := readInt(), readInt()
		for j := w - ww; j >= 0; j-- {
			dp[j+ww] = max(dp[j+ww], dp[j]+v)
		}
	}

	fmt.Println(dp[w])
}

// sort ------------------------------------------------------------

type xxx struct {
	x int
}

type sortArray []xxx

func (s sortArray) Len() int           { return len(s) }
func (s sortArray) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s sortArray) Less(i, j int) bool { return s[i].x < s[j].x }

// -----------------------------------------------------------------

var (
	readString func() string
	readBytes  func() []byte
)

func init() {
	log.SetFlags(log.Lshortfile)
	readString, readBytes = newReadString(os.Stdin)
}

func newReadString(ior io.Reader) (func() string, func() []byte) {
	r := bufio.NewScanner(ior)
	r.Buffer(make([]byte, 1024), int(1e+11))
	r.Split(bufio.ScanWords)

	f1 := func() string {
		if !r.Scan() {
			panic("Scan failed")
		}
		return r.Text()
	}
	f2 := func() []byte {
		if !r.Scan() {
			panic("Scan failed")
		}
		return r.Bytes()
	}
	return f1, f2
}

func readInt() int {
	return int(readInt64())
}

func readInt64() int64 {
	i, err := strconv.ParseInt(readString(), 10, 64)
	if err != nil {
		panic(err.Error())
	}
	return i
}

func readFloat64() float64 {
	f, err := strconv.ParseFloat(readString(), 64)
	if err != nil {
		panic(err.Error())
	}
	return f
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func abs(a int) int {
	if a < 0 {
		return -a
	}
	return a
}

func sum(a []int) int {
	var ret int
	for i := range a {
		ret += a[i]
	}
	return ret
}

func sumFloat64(a []float64) float64 {
	var ret float64
	for i := range a {
		ret += a[i]
	}
	return ret
}

func gcd(m, n int) int {
	for m%n != 0 {
		m, n = n, m%n
	}
	return n
}

func lcm(m, n int) int {
	return m / gcd(m, n) * n
}
//  [go]
package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"math/big"
	"os"
)

var reader *bufio.Reader
var writer *bufio.Writer

var inputFile = flag.String("input", "", "")

func init() {
	flag.Parse()
	var i io.Reader
	if *inputFile != "" {
		i, _ = os.Open(*inputFile)
	} else {
		i = os.Stdin
	}
	reader = bufio.NewReaderSize(i, 1<<20)
	writer = bufio.NewWriterSize(os.Stdout, 1<<20)
}

func println(a ...interface{}) {
	_, _ = fmt.Fprintln(writer, a...)
}

func printf(format string, a ...interface{}) {
	_, _ = fmt.Fprintf(writer, format, a...)
}

func scanf(format string, a ...interface{}) {
	_, _ = fmt.Fscanf(reader, format, a...)
}

func scan(a interface{}) {
	_, _ = fmt.Fscan(reader, a)
}

func NextInt64() int64 {
	var res int64
	scan(&res)
	return res
}

func NextInt() int {
	var res int
	scan(&res)
	return res
}

func NextIntArray(n int) []int {
	res := make([]int, n)
	for i := range res {
		scan(&res[i])
	}
	return res
}

func NextInt64Array(n int) []int64 {
	res := make([]int64, n)
	for i := range res {
		scan(&res[i])
	}
	return res
}

func NextString() string {
	var res string
	scan(&res)
	return res
}

func NextBigInt() *big.Int {
	n := new(big.Int)
	n, _ = n.SetString(NextString(), 10)
	return n
}

func AbsInt64(x int64) int64 {
	if x < 0 {
		return -x
	}
	return x
}

func MinInt64(x, y int64) int64 {
	if x < y {
		return x
	}
	return y
}

func MaxInt64(x, y int64) int64 {
	if x > y {
		return x
	}
	return y
}

func main() {
	defer writer.Flush()

	n := NextInt()
	W := NextInt()

	dp := make([]int64, W + 1)

	for i := 0; i < n; i++ {
		w := NextInt()
		v := NextInt64()
		
		for j := W; j >= w; j-- {
			dp[j] = MaxInt64(dp[j], dp[j - w] + v)
		}
	}
	
	answer := int64(0)
	for i := 0; i <= W; i++ {
		answer = MaxInt64(answer, dp[i])
	}
	println(answer)
}
//  [go]
package maihn

import (
	"fmt"
	"math"
)

func main() {
	var n, w int64
	fmt.Scanf("%d%d", &n, &w)
	weight := make([]int64, n)
	values := make([]int64, n)
	for i := 0; i < int(n); i++ {
		fmt.Scanf("%d%d", &weight[i], &values[i])
	}
	ans := maximumPossibleSum(n, w, values, weight)
	fmt.Println(ans)
}

// https://atcoder.jp/contests/dp/tasks/dp_d
func maximumPossibleSum(n int64, w int64, values []int64, weight []int64) int64 {
	dp := make([][]int64, n+1)
	for i := 0; i <= int(n); i++ {
		dp[i] = make([]int64, w+1)
	}
	for i := 1; i <= int(n); i++ {
		for j := 1; j <= int(w); j++ {
			if weight[i-1] > int64(j) {
				dp[i][j] = dp[i-1][j]
			} else {
				dp[i][j] = int64(math.Max(float64(dp[i-1][j]), float64(values[i-1]+dp[i-1][int64(j)-weight[i-1]])))
			}
		}
	}
	return dp[n][w]
}
//  [go]
//go:generate echo "https://atcoder.jp/contests/dp/tasks/dp_d"
package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strconv"
)

type scanner struct{ *bufio.Scanner }

func newScanner(r io.Reader) *scanner {
	s := bufio.NewScanner(r)
	s.Split(bufio.ScanWords)
	s.Buffer(nil, 100000000)
	return &scanner{s}
}

func (s *scanner) Int() int {
	s.Scan()
	n, _ := strconv.Atoi(s.Text())
	return n
}

func (s *scanner) IntSlice(l int) []int {
	if l == 0 {
		return []int{}
	}
	sl := make([]int, l, l)
	for i := range sl {
		sl[i] = s.Int()
	}
	return sl
}

func chmax(x *int, v int) {
	if *x < v {
		*x = v
	}
}

func solve(N, W int, wvs [][]int) int {
	t := make([][]int, N+1, N+1) // t[item_index][weight]
	for i := range t {
		t[i] = make([]int, W+1, W+1)
	}
	for i, wv := range wvs {
		weight, value := wv[0], wv[1]
		for sumWeight := 0; sumWeight <= W; sumWeight++ {
			if sumWeight-weight >= 0 {
				chmax(&t[i+1][sumWeight], t[i][sumWeight-weight]+value)
			}
			chmax(&t[i+1][sumWeight], t[i][sumWeight])
		}
	}
	return t[N][W]
}

func main() {
	for {

	}
	scan := newScanner(os.Stdin)
	N, W := scan.Int(), scan.Int()
	wvs := make([][]int, N, N)
	for i := 0; i < N; i++ {
		wvs[i] = []int{scan.Int(), scan.Int()}
	}
	fmt.Println(solve(N, W, wvs))
}
//  [ruby]
WV = $<.read.split.map(&:to_i).each_slice(2).to_a
N,W = WV.shift
WV.sort_by! {|w,v| [w,-v]}
I = {}
WV.each.with_index { |(w,*),i| break if w > W; I[w] ||= i}
I[0] = N
K = I.keys
A = Array.new W+1
A[0] = [0,0]
l = [-1]
A.each_with_index {|(c,b),n|
  if (c||0) <= l[0] then next A[n]=l else l=A[n] end
  K.each_cons(2) {|w,x|
    i,j = I[w],I[x]
    break  if W < w+=n
    m = 1 << j
    next  if m <= (b&(m-1))+(1<<i)
	i += 1  while b[i] > 0
    m = (A[w]||l)[0]
    v = WV[i][1] + c
    A[w] = [v,b+(1<<i)]  if m < v
  }
}
p A[W][0]
//  [ruby]
WV = $<.read.split.map(&:to_i).each_slice(2).to_a
N,W = WV.shift
WV.sort_by! {|w,v| [w,-v]}
I = {}
WV.each.with_index { |(w,*),i| break if w > W; I[w] ||= i}
I[0] = N
K = I.keys
A = Array.new W+1
A[0] = [0,0]
l = [-1]
A.each_with_index {|(c,b),n|
  (c||0) > l[0] ? l=A[n] : next
  K.each_cons(2) {|w,x|
    i,j = I[w],I[x]
    break  if W < w+=n
    m = 1 << j
    next  if WV[i][1]+c <= l[0] || m <= (b&(m-1))+(1<<i)
	i += 1  while b[i] > 0
    m = (A[w]||l)[0]
    v = WV[i][1] + c
    A[w] = [v,b+(1<<i)]  if m < v
  }
}
p l[0]
//  [ruby]
N, W = gets.split.map(&:to_i)
d = [[0, 0]]

(0..(N-1)).each do
  w, v = gets.split.map(&:to_i)
  next if w > W

  (0..(d.size - 1)).each do |i|
    d << [d[i][0] + w, d[i][1] + v] if d[i][0] + w <= W
  end

  d.sort!

  idx = 1
  while idx < d.size do
    if d[idx-1][1] >= d[idx][1]
      d.delete_at(idx)
    else
      idx += 1
    end
  end
end

puts d[-1][1]
//  [ruby]
N, W = gets.split.map(&:to_i)

w = N.times.map { gets.split.map(&:to_i) }

dp = []
dp[0] = [0] * (W + 1)

1.upto(N) do |i|
  dp[i] = (W + 1).times.map { 0 }
  0.upto(W) do |j|
    if j >= w[i - 1][0]
      dp[i][j] = [dp[i - 1][j - w[i - 1][0]] + w[i - 1][1], dp[i - 1][j]].max
    else
      dp[i][j] = dp[i - 1][j]
    end
  end
end

puts dp[N][W]
//  [ruby]
F =-> {gets.split.map &:to_i}
N,W = F[]
D = Hash.new 0
D[0] = 0
N.times {
	w,v = F[]
	D.keys.each {|i| W >= j=i+w and D[j] < x=D[i]+v and D[j]=x}
}
p D.values.max
//  [ruby]
n, w = $stdin.readline.chomp.split.map(&:to_i)
dp = Array.new(n + 1) { Hash.new(0) }
dp[0][w] = 0

n.times do |i|
  wi, vi = $stdin.readline.chomp.split.map(&:to_i)
  dp[i].each do |k, v|
    dp[i + 1][k] = [dp[i + 1][k], v].max
    if k - wi >= 0
      dp[i + 1][k - wi] = [dp[i + 1][k - wi], v + vi].max
    end
  end  
end
puts dp[n].values.max
//  [rust]
// Try Educational DP Contest
// author: Leonardone @ NEETSDKASU

fn main() {
	let mut stdin = String::new();
	std::io::Read::read_to_string(
		&mut std::io::stdin(),
		&mut stdin).unwrap();
	let mut stdin = stdin.split_whitespace();
	let mut get = || stdin.next().unwrap();
	macro_rules! get {
		($t:ty) => (get().parse::<$t>().unwrap());
		() => (get!(i64));
	}
	
	let n = get!(usize);
	let w = get!(usize);
	let mut dp = vec![0; w + 1];
	for _ in 0..n {
		let wi = get!(usize);
		let vi = get!();
		for j in 0..w-wi+1 {
			let j = w-wi-j;
			dp[j+wi] = std::cmp::max(
				dp[j+wi], dp[j]+vi);
		}
	}
	let ans = dp.iter().max().unwrap();
	println!("{}", ans);
}
//  [rust]
use std::io::*;
use std::str::*;
use std::collections::*;

fn main() {
    let N : i32 = read();
    let W : usize = read();
    let mut dp = vec![0 as usize; W + 1];
    for i in 0..N {
        let w : i32 = read();
        let v : usize = read();
        for j in (0..W+1).rev() {
            let pj = j as i32 - w;
            if pj < 0 { break; }
            let pj = pj as usize;
            dp[j] = std::cmp::max(dp[j], dp[pj] + v);
        }
    }
    println!("{}", dp[W]);
}

fn read<T: FromStr>() -> T {
    let cin = stdin();
    let cin = cin.lock();
    let s: String = cin 
        .bytes() // Bytes
        .map(|c| c.expect("failed reading char") as char)
        .skip_while(|c| c.is_whitespace()) // c が whitespace である限り skip
        .take_while(|c| !c.is_whitespace()) // c が whitespace でない限り要素を返す
        .collect();
    s.parse().ok().expect("failed parsing")
}
//  [rust]
macro_rules ! input { ( source = $ s : expr , $ ( $ r : tt ) * ) => { let mut iter = $ s . split_whitespace ( ) ; input_inner ! { iter , $ ( $ r ) * } } ; ( $ ( $ r : tt ) * ) => { let s = { use std :: io :: Read ; let mut s = String :: new ( ) ; std :: io :: stdin ( ) . read_to_string ( & mut s ) . unwrap ( ) ; s } ; let mut iter = s . split_whitespace ( ) ; input_inner ! { iter , $ ( $ r ) * } } ; }
macro_rules ! input_inner { ( $ iter : expr ) => { } ; ( $ iter : expr , ) => { } ; ( $ iter : expr , mut $ var : ident : $ t : tt $ ( $ r : tt ) * ) => { let mut $ var = read_value ! ( $ iter , $ t ) ; input_inner ! { $ iter $ ( $ r ) * } } ; ( $ iter : expr , mut $ var : ident $ ( $ r : tt ) * ) => { let mut $ var = $ iter . next ( ) . unwrap ( ) . parse ::< usize > ( ) . unwrap ( ) ; input_inner ! { $ iter $ ( $ r ) * } } ; ( $ iter : expr , $ var : ident : $ t : tt $ ( $ r : tt ) * ) => { let $ var = read_value ! ( $ iter , $ t ) ; input_inner ! { $ iter $ ( $ r ) * } } ; ( $ iter : expr , $ var : ident $ ( $ r : tt ) * ) => { let $ var = $ iter . next ( ) . unwrap ( ) . parse ::< usize > ( ) . unwrap ( ) ; input_inner ! { $ iter $ ( $ r ) * } } ; }
macro_rules ! read_value { ( $ iter : expr , ( $ ( $ t : tt ) ,* ) ) => { ( $ ( read_value ! ( $ iter , $ t ) ) ,* ) } ; ( $ iter : expr , [ $ t : tt ; $ len : expr ] ) => { ( 0 ..$ len ) . map ( | _ | read_value ! ( $ iter , $ t ) ) . collect ::< Vec < _ >> ( ) } ; ( $ iter : expr , chars ) => { read_value ! ( $ iter , String ) . chars ( ) . collect ::< Vec < char >> ( ) } ; ( $ iter : expr , usize1 ) => { read_value ! ( $ iter , usize ) - 1 } ; ( $ iter : expr , $ t : ty ) => { $ iter . next ( ) . unwrap ( ) . parse ::<$ t > ( ) . unwrap ( ) } ; }
use std::cmp::max;
fn main() {
    input! { n, cap, wv: [(usize, usize); n] };
    let mut dp = vec![0; cap + 2];
    for &(w, v) in &wv {
        for c in (0..(cap + 1)).rev() {
            if c + w <= cap {
                dp[c + w] = max(dp[c + w], dp[c] + v);
            }
        }
    }
    println!("{}", dp.iter().max().unwrap());
}
//  [rust]
macro_rules! input {
    (source = $s:expr, $($r:tt)*) => {
        let mut iter = $s.split_whitespace();
        input_inner!{iter, $($r)*}
    };
    ($($r:tt)*) => {
        #[allow(unused_mut)]
        let mut s = {
            use std::io::Read;
            let mut s = String::new();
            std::io::stdin().read_to_string(&mut s).unwrap();
            s
        };
        let mut iter = s.split_whitespace();
        input_inner!{iter, $($r)*}
    };
}

macro_rules! input_inner {
    ($iter:expr) => {};
    ($iter:expr, ) => {};

    ($iter:expr, $var:ident : $t:tt $($r:tt)*) => {
        let $var = read_value!($iter, $t);
        input_inner!{$iter $($r)*}
    };
}

macro_rules! read_value {
    ($iter:expr, ( $($t:tt),* )) => {
        ( $(read_value!($iter, $t)),* )
    };

    ($iter:expr, [ $t:tt ; $len:expr ]) => {
        (0..$len).map(|_| read_value!($iter, $t)).collect::<Vec<_>>()
    };

    ($iter:expr, chars) => {
        read_value!($iter, String).chars().collect::<Vec<char>>()
    };

    ($iter:expr, usize1) => {
        read_value!($iter, usize) - 1
    };

    ($iter:expr, $t:ty) => {
        $iter.next().unwrap().parse::<$t>().expect("Parse error")
    };
}

use std::collections::HashMap;

#[derive(Debug)]
struct Load {
    pub weight: i64,
    pub value: i64,
}

fn main() {
    input! {
        n: i64,
        w: i64,
        _loads: [(i64, i64); n as usize]
    }
    let mut loads = vec![];
    for l in _loads {
        loads.push(Load {
            weight: l.0,
            value: l.1,
        })
    }
    let mut cache = HashMap::new();
    let v = max_value(&loads, &mut cache, n - 1, w);
    println!("{}", v)
}

fn max_value(loads: &Vec<Load>, cache: &mut HashMap<(i64, i64), i64>, k: i64, y: i64) -> i64 {
    if let Some(v) = cache.get(&(k, y)) {
        return *v;
    }

    if k < 0 {
        0
    } else {
        let using = if y - loads[k as usize].weight < 0 {
            0
        } else {
            max_value(loads, cache, k - 1, y - loads[k as usize].weight) + loads[k as usize].value
        };
        let not_using = max_value(loads, cache, k - 1, y);
        let value = i64::max(using, not_using);
        cache.insert((k, y), value);
        value
    }
}
//  [rust]
macro_rules! input {
    (source = $s:expr, $($r:tt)*) => {
        let mut iter = $s.split_whitespace();
        input_inner!{iter, $($r)*}
    };
    ($($r:tt)*) => {
        #[allow(unused_mut)]
        let mut s = {
            use std::io::Read;
            let mut s = String::new();
            std::io::stdin().read_to_string(&mut s).unwrap();
            s
        };
        let mut iter = s.split_whitespace();
        input_inner!{iter, $($r)*}
    };
}

macro_rules! input_inner {
    ($iter:expr) => {};
    ($iter:expr, ) => {};

    ($iter:expr, $var:ident : $t:tt $($r:tt)*) => {
        let $var = read_value!($iter, $t);
        input_inner!{$iter $($r)*}
    };
}

macro_rules! read_value {
    ($iter:expr, ( $($t:tt),* )) => {
        ( $(read_value!($iter, $t)),* )
    };

    ($iter:expr, [ $t:tt ; $len:expr ]) => {
        (0..$len).map(|_| read_value!($iter, $t)).collect::<Vec<_>>()
    };

    ($iter:expr, chars) => {
        read_value!($iter, String).chars().collect::<Vec<char>>()
    };

    ($iter:expr, usize1) => {
        read_value!($iter, usize) - 1
    };

    ($iter:expr, $t:ty) => {
        $iter.next().unwrap().parse::<$t>().expect("Parse error")
    };
}

use std::collections::HashMap;

#[derive(Debug)]
struct Load {
    pub weight: i64,
    pub value: i64,
}

fn main() {
    input! {
        n: i64,
        w: i64,
        _loads: [(i64, i64); n as usize]
    }
    let mut loads = vec![];
    for l in _loads {
        loads.push(Load {
            weight: l.0,
            value: l.1,
        })
    }
    let mut cache = HashMap::new();
    let v = max_value(&loads, &mut cache, n - 1, w);
    println!("{}", v)
}

fn max_value(loads: &Vec<Load>, cache: &mut HashMap<(i64, i64), i64>, k: i64, y: i64) -> i64 {
    if let Some(v) = cache.get(&(k, y)) {
        return *v;
    }

    if k < 0 {
        0
    } else if y - loads[k as usize].weight < 0 {
        max_value(loads, cache, k - 1, y)
    } else {
        let value = i64::max(
            max_value(loads, cache, k - 1, y - loads[k as usize].weight) + loads[k as usize].value,
            max_value(loads, cache, k - 1, y),
        );
        cache.insert((k, y), value);
        value
    }
}
//  [rust]
macro_rules! input {
    (source = $s:expr, $($r:tt)*) => {
        let mut iter = $s.split_whitespace();
        input_inner!{iter, $($r)*}
    };
    ($($r:tt)*) => {
        #[allow(unused_mut)]
        let mut s = {
            use std::io::Read;
            let mut s = String::new();
            std::io::stdin().read_to_string(&mut s).unwrap();
            s
        };
        let mut iter = s.split_whitespace();
        input_inner!{iter, $($r)*}
    };
}

macro_rules! input_inner {
    ($iter:expr) => {};
    ($iter:expr, ) => {};

    ($iter:expr, $var:ident : $t:tt $($r:tt)*) => {
        let $var = read_value!($iter, $t);
        input_inner!{$iter $($r)*}
    };
}

macro_rules! read_value {
    ($iter:expr, ( $($t:tt),* )) => {
        ( $(read_value!($iter, $t)),* )
    };

    ($iter:expr, [ $t:tt ; $len:expr ]) => {
        (0..$len).map(|_| read_value!($iter, $t)).collect::<Vec<_>>()
    };

    ($iter:expr, chars) => {
        read_value!($iter, String).chars().collect::<Vec<char>>()
    };

    ($iter:expr, usize1) => {
        read_value!($iter, usize) - 1
    };

    ($iter:expr, $t:ty) => {
        $iter.next().unwrap().parse::<$t>().expect("Parse error")
    };
}

use std::collections::HashMap;

#[derive(Debug)]
struct Load {
    pub weight: i64,
    pub value: i64,
}

fn main() {
    input! {
        n: i64,
        w: i64,
        _loads: [(i64, i64); n as usize]
    }
    let mut loads = vec![];
    for l in _loads {
        loads.push(Load {
            weight: l.0,
            value: l.1,
        })
    }
    let mut cache = HashMap::new();
    let v = max_value(&loads, &mut cache, n - 1, w);
    println!("{}", v)
}

fn max_value(loads: &Vec<Load>, cache: &mut HashMap<(i64, i64), i64>, k: i64, y: i64) -> i64 {
    if k < 0 {
        0
    } else if y - loads[k as usize].weight < 0 {
        max_value(loads, cache, k - 1, y)
    } else {
        if let Some(v) = cache.get(&(k, y)) {
            *v
        } else {
            let value = i64::max(
                max_value(loads, cache, k - 1, y - loads[k as usize].weight)
                    + loads[k as usize].value,
                max_value(loads, cache, k - 1, y),
            );
            cache.insert((k, y), value);
            value
        }
    }
}
//  [swift]
import Foundation

func readWords<T>(_ f: (Substring) -> T) -> [T] {
    return readLine()!.split(separator: " ").map(f)
}

func main() {
    let nw = readWords { Int($0)! }
    let n = nw[0]
    let w = nw[1]

    let items: [(w: Int, v: Int)] = (1...n).map { _ in 
        let wv = readWords { Int($0)! }
        return (w: wv[0], v: wv[1])    
    }
    var dp: [[Int]] = (0...n).map {_ in [Int](repeating: 0, count: w+1) }

    for i in 0..<n {
        for weight in 0...w {
            if items[i].w <= weight {
                dp[i+1][weight] = max(dp[i][weight-items[i].w] + items[i].v, dp[i][weight])
            } else {
                dp[i+1][weight] = dp[i][weight]
            }
        }
    }

    print(dp[n][w])
}


main()
//  [swift]
/// Swift5 -> Swift2.2
 
extension String {
    // split(separator:maxSplits:omittingEmptySubsequences:)
    func split(separator separator: Character) -> [String] {
        return self.characters.split(separator).map(String.init)
    }
}
extension Array {
    // init(repeating:count:)
    init(repeating: Element, count: Int) {
        self.init(count: count, repeatedValue: repeating)
    }
}
extension Array where Element: Comparable {
    // sorted()
    func sorted() -> [Element] {
        return self.sort( { $0 < $1 } )
    }
}
// stride(from:through:by:)
func stride(from start: Int, through end: Int, by stride: Int) -> StrideThrough<Int> {
    return start.stride(through: end, by: stride)
}
// stride(from:to:by)
func stride(from start: Int, to end: Int, by stride: Int) -> StrideTo<Int> {
    return start.stride(to: end, by: stride)
}
extension Range {
    func reversed() -> [Element] {
        return self.reverse()
    }
}
 
/// end
 
 
let input = readLine()!.split(separator: " ").map { Int($0)! }
let N = input[0]
let W = input[1]
 
var dp = Array(repeating: 0, count: W + 1)
 
for _ in 0 ..< N {
    let knapsack = readLine()!.split(separator: " ").map { Int($0)! }
    let weight = knapsack[0]
    let value = knapsack[1]
 
  	var i = W
  	while i >= weight {
        dp[i] = max(dp[i], dp[i - weight] + value)
        i -= 1
    }
}
 
print(dp.last!)
//  [swift]

/// Swift5 -> Swift2.2
extension String {
    // split(separator:maxSplits:omittingEmptySubsequences:)
    func split(separator separator: Character) -> [String] {
        return self.characters.split(separator).map(String.init)
    }
}
extension Array {
    // init(repeating:count:)
    init(repeating: Element, count: Int) {
        self.init(count: count, repeatedValue: repeating)
    }
}
extension Array where Element: Comparable {
    // sorted()
    func sorted() -> [Element] {
        return self.sort( { $0 < $1 } )
    }
}
// stride(from:through:by:)
func stride(from start: Int, through end: Int, by stride: Int) -> StrideThrough<Int> {
    return start.stride(through: end, by: stride)
}
// stride(from:to:by)
func stride(from start: Int, to end: Int, by stride: Int) -> StrideTo<Int> {
    return start.stride(to: end, by: stride)
}
/// end

let input = readLine()!.split(separator: " ").map { Int($0)! }
let N = input[0]
let W = input[1]

var dp = Array(repeating: 0, count: W + 1)

for _ in 0 ..< N {
    let knapsack = readLine()!.split(separator: " ").map { Int($0)! }
    let weight = knapsack[0]
    let value = knapsack[1]

    let tmp = Array((weight ... W).reverse())
    for i in stride(from: W, through: weight, by: -1) {
        dp[i] = max(dp[i], dp[i - weight] + value)
    }
}

print(dp.last!)
//  [swift]

/// Swift5 -> Swift2.2

extension String {
    // split(separator:maxSplits:omittingEmptySubsequences:)
    func split(separator separator: Character) -> [String] {
        return self.characters.split(separator).map(String.init)
    }
}
extension Array {
    // init(repeating:count:)
    init(repeating: Element, count: Int) {
        self.init(count: count, repeatedValue: repeating)
    }
}
extension Array where Element: Comparable {
    // sorted()
    func sorted() -> [Element] {
        return self.sort( { $0 < $1 } )
    }
}
// stride(from:through:by:)
func stride(from start: Int, through end: Int, by stride: Int) -> StrideThrough<Int> {
    return start.stride(through: end, by: stride)
}
// stride(from:to:by)
func stride(from start: Int, to end: Int, by stride: Int) -> StrideTo<Int> {
    return start.stride(to: end, by: stride)
}

/// end

let input = readLine()!.split(separator: " ").map { Int($0)! }
let N = input[0]
let W = input[1]

var dp = Array(repeating: 0, count: W + 1)

for _ in 0 ..< N {
    let knapsack = readLine()!.split(separator: " ").map { Int($0)! }
    let weight = knapsack[0]
    let value = knapsack[1]

    for i in (weight ... W).reverse() {
        dp[i] = max(dp[i], dp[i - weight] + value)
    }
}

print(dp.last!)
//  [swift]

/// Swift5 -> Swift2.2

extension String {
    // split(separator:maxSplits:omittingEmptySubsequences:)
    func split(separator separator: Character) -> [String] {
        return self.characters.split(separator).map(String.init)
    }
}
extension Array {
    // init(repeating:count:)
    init(repeating: Element, count: Int) {
        self.init(count: count, repeatedValue: repeating)
    }
}
extension Array where Element: Comparable {
    // sorted()
    func sorted() -> [Element] {
        return self.sort( { $0 < $1 } )
    }
}
// stride(from:through:by:)
func stride(from start: Int, through end: Int, by stride: Int) -> StrideThrough<Int> {
    return start.stride(through: end, by: stride)
}
// stride(from:to:by)
func stride(from start: Int, to end: Int, by stride: Int) -> StrideTo<Int> {
    return start.stride(to: end, by: stride)
}
extension Range {
    func reversed() -> [Element] {
        return self.reverse()
    }
}

/// end

let input = readLine()!.split(separator: " ").map { Int($0)! }
let N = input[0]
let W = input[1]

var dp = Array(repeating: 0, count: W + 1)

for _ in 0 ..< N {
    let knapsack = readLine()!.split(separator: " ").map { Int($0)! }
    let weight = knapsack[0]
    let value = knapsack[1]

  	let tmp: [Int] = (weight ... W).reversed()
    for i in stride(from: W, through: weight, by: -1) {
        dp[i] = max(dp[i], dp[i - weight] + value)
    }
}

print(dp.last!)
//  [typescript]
import * as fs from "fs";

// inputで受け取り配列化する
const input = fs.readFileSync("/dev/stdin", "utf8").split("\n");
const [n, w] = input.shift().split(" ").map(Number);
const items = input.map((item) => item.split(" ").map(Number));

const dp = [];
for (let i = 0; i <= n; i++) {
  const arr = [];
  for (let j = 0; j <= w; j++) {
    arr.push(0);
  }
  dp.push(arr);
}

for (let index = 0; index < n; index++) {
  for (let sum_weight = 0; sum_weight <= w; sum_weight++) {
    const item = items[index];
    if (sum_weight - item[0] >= 0) {
      dp[index + 1][sum_weight] = Math.max(
        dp[index + 1][sum_weight],
        dp[index][sum_weight - item[0]] + item[1]
      );
    }
    dp[index + 1][sum_weight] = Math.max(
      dp[index + 1][sum_weight],
      dp[index][sum_weight]
    );
  }
}

console.log(dp[n][w]);
//  [typescript]
import * as fs from "fs";

// inputで受け取り配列化する
const input = fs.readFileSync("/dev/stdin", "utf8").split("\n");
const [n, w] = input.shift().split(" ").map(Number);
const items = input.map((item) => item.split(" ").map(Number));

const dp = new Map();

const dig = (index: number, weight: number, val: number): number => {
  if (weight > w) {
    return 0;
  } else if (index === n) {
    return val;
  }
  if (dp.has(`${index}${weight}`)) {
    return dp.get(`${index}${weight}`);
  }

  const item = items[index];
  const newVal = Math.max(
    dig(index + 1, weight, val),
    dig(index + 1, weight + item[0], val + item[1])
  );
  dp.set(`${index}${weight}`, newVal);
  return newVal;
};

console.log(dig(0, 0, 0));
//  [typescript]
import * as fs from "fs"

// 配列を作成して指定された値で初期化する
const createArray = (length: number, initialValue: any): any[] => {
  const isArray = <T>(value: any): value is Array<T> => Array.isArray(value)
  const ary: any[] = []
  for (let i = 0; i < length; i++) {
    if (isArray(initialValue)) {
      // TODO 多分ディープコピーしたほうがいいやつ
      ary.push([...initialValue])
    } else if (typeof initialValue === "object") {
      // TODO 多分ディープコピーしたほうがいいやつ
      ary.push({ ...initialValue })
    } else {
      ary.push(initialValue)
    }
  }
  return ary
}

const process = (n: number, w: number, wv: number[][]): string => {
  const table: number[][] = createArray(n + 1, createArray(w + 1, 0))

  for (let i = 1; i < n + 1; i++) {
    const wi = wv[i - 1][0]
    const vi = wv[i - 1][1]

    for (let j = 1; j < w + 1; j++) {
      // i 番目の品物を選ぶ
      if (j < wi) {
        table[i][j] = table[i - 1][j]
        continue
      }
      if (table[i - 1][j] < table[i - 1][j - wi] + vi) {
        // 更新
        table[i][j] = table[i - 1][j - wi] + vi
      } else {
        table[i][j] = table[i - 1][j]
      }
    }
  }
  console.log(table)

  return table[n][w] + ""
}

const Main = (input: string): string => {
  const aa: string[][] = input.split("\n").map((e) => e.split(" "))
  const na0: number[] = aa[0].map((e) => parseInt(e))
  aa.shift()
  const na: number[][] = aa.map((e) => e.map((e) => parseInt(e)))
  const [a, b] = na0

  const res = process(a, b, na) + ""
  console.log(res)
  return res
}
Main(fs.readFileSync("/dev/stdin", "utf8"))
//  [typescript]
import * as fs from "fs"

// 配列を作成して指定された値で初期化する
const createArray = (length: number, initialValue: any): any[] => {
  const isArray = <T>(value: any): value is Array<T> => Array.isArray(value)
  const ary: any[] = []
  for (let i = 0; i < length; i++) {
    if (isArray(initialValue)) {
      // TODO 多分ディープコピーしたほうがいいやつ
      ary.push([...initialValue])
    } else if (typeof initialValue === "object") {
      // TODO 多分ディープコピーしたほうがいいやつ
      ary.push({ ...initialValue })
    } else {
      ary.push(initialValue)
    }
  }
  return ary
}

const process = (n: number, w: number, wv: number[][]): string => {
  const table: number[][] = createArray(n, createArray(w, 0))

  for (let i = 0; i < n; i++) {
    // i：いくつめ？
    const wi = wv[i][0] - 1
    const vi = wv[i][1]

    for (let j = 0; j < w; j++) {
      if (i === 0) {
        if (j >= wi) {
          table[i][j] = vi
        }
        continue
      }

      // j：そのときの w
      if (j <= wi) {
        table[i][j] = table[i - 1][j]
        continue
      }

      if (table[i - 1][j] < table[i - 1][j - wi] + vi) {
        // 更新
        table[i][j] = table[i - 1][j - wi] + vi
      } else {
        table[i][j] = table[i - 1][j]
      }
    }
  }

  return table[n - 1][w - 1] + ""
}

const Main = (input: string): string => {
  const aa: string[][] = input.split("\n").map((e) => e.split(" "))
  const na0: number[] = aa[0].map((e) => parseInt(e))
  aa.shift()
  const na: number[][] = aa.map((e) => e.map((e) => parseInt(e)))
  const [a, b] = na0

  const res = process(a, b, na) + ""
  console.log(res)
  return res
}
Main(fs.readFileSync("/dev/stdin", "utf8"))