#include <iostream>
#include <vector>
using namespace std;
// 背包数量有限
//
// 算法: 三重循环, 最后对背包使用数量进行枚举
//
// 但是这里数据量大了会超时 -> 使用二进制优化
//
// 基本思路: 选了n个物品其实相当于选了 n个1个物品,
// 所以我们将{1..n}个物品都当成单独的一个可选物品 问题就转换成了二维了01背包问题
// 但是如果s个物品都拆成1 2 .. s个物品的话数据量其实是不变的n * m * s
//
// 假设能拆成1 2 3, 观察到, 三其实的没有必要的, 因为如果选了1
// 2两个就等价于选了一个3
//
// 参考二进制的表示, 任意数都可以表示成, 若干"2^n"的和, 如7 = "111" = 4 + 2 + 1,
// 3 = "11" = 2 + 1 所以只需差分成1 2 4 8等就能表示任意组合了
//
// 对于非2整次幂的数量我们可以拆成什么, 如101, 如果拆成1 2 4,
// 那么能够表示7就超出了数量限制6了 我们可以"抛弃最高位, 补足offset", 如10 = 7
// + 3 = 拆成 1 2 4, 3。1 2
// 4可以表示0~7的任意数在加上3的话最多最多就能表示到10了
//
// 且必须优化成一维
//
#define N 2010
struct Goods {
int v, w;
};
int dp[N];
int main() {
int n, m;
vector<Goods> gd;
gd.push_back({0, 0});
cin >> n >> m;
for (int i = 1; i <= n; i++) {
int v, w, s;
cin >> v >> w >> s;
for (int k = 1; k <= s; k *= 2) {
s -= k;
gd.push_back({k * v, k * w});
}
if (s > 0)
gd.push_back({s * v, s * w});
}
n = gd.size() - 1;
for (int i = 1; i <= n; i++) {
for (int j = m; j >= gd[i].v; j--) {
dp[j] = max(dp[j], dp[j - gd[i].v] + gd[i].w);
}
}
cout << dp[m];
return 0;
}