package main

import (
	"bufio"
	"fmt"
	"os"
	"sort"
	"strconv"
	"strings"
)

func parseInt(s string) int {
	return int(parseInt64(s))
}

func parseInt64(s string) int64 {
	x, _ := strconv.ParseInt(s, 10, 64)
	return x
}

var reader *bufio.Reader

func readLine() string {
	if reader == nil {
		reader = bufio.NewReader(os.Stdin)
	}
	s, _ := reader.ReadString('\n')
	return strings.TrimSuffix(s, "\n")
}

func readFields() []string {
	return strings.Fields(readLine())
}

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

func max64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

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

func sum(a []int) int {
	total := 0
	for _, x := range a {
		total += x
	}
	return total
}

var CYCLE int64 = 1000000007

type Block struct {
	w int
	s int
	v int64
}

var N int
var blocks [1000]Block

type SortableBlocks []Block

func (a SortableBlocks) Len() int           { return len(a) }
func (a SortableBlocks) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a SortableBlocks) Less(i, j int) bool { return a[i].w+a[i].s < a[j].w+a[j].s }

// Segment Tree begin

type T int64

type SegmentTree struct {
	size        int
	depth       int
	data        [][]T
	delayedData [][]T
	dirty       [][]bool
}

var INVALID T = 0

func (t SegmentTree) reduce(a, b T) T {
	if a > b {
		return a
	} else {
		return b
	}
}

func (t SegmentTree) _query(s, e, d, i int) T {
	if t.delayedData[d][i] != 0 {
		dd := t.delayedData[d][i]
		t.delayedData[d][i] = 0
		t.data[d][i] += dd
		if d > 0 {
			t.delayedData[d-1][i<<1] += dd
			t.delayedData[d-1][(i<<1)+1] += dd
		}
	}

	step := 1 << uint(d)
	ss := i * step
	ee := ss + step
	if ee <= s || e <= ss {
		return INVALID
	} else if ss < s || e < ee || t.dirty[d][i] {
		t.data[d][i] = t.reduce(t._query(s, e, d-1, i<<1), t._query(s, e, d-1, (i<<1)+1))
		t.dirty[d][i] = false
	}
	return t.data[d][i]
}

func (t SegmentTree) query(s, e int) T {
	return t._query(s, e, t.depth-1, 0)
}

func (t SegmentTree) _addRange(s, e, d, i int, value T) {
	step := 1 << uint(d)
	ss := i * step
	ee := ss + step
	if ee <= s || e <= ss {
		// Nothing to do
	} else if s <= ss && ee <= e {
		t.delayedData[d][i] += value
	} else {
		t._addRange(s, e, d-1, i<<1, value)
		t._addRange(s, e, d-1, (i<<1)+1, value)
		t.dirty[d][i] = true
	}
}

func (t SegmentTree) addRange(s, e int, value T) {
	t._addRange(s, e, t.depth-1, 0, value)
}

func newSegmentTree(size int) SegmentTree {
	var t SegmentTree
	t.size = size

	t.depth = 1
	for k := size - 1; k > 0; k >>= 1 {
		t.depth++
	}

	t.data = make([][]T, t.depth)
	t.delayedData = make([][]T, t.depth)
	t.dirty = make([][]bool, t.depth)
	for d := t.depth; d > 0; d-- {
		w := 1 << uint(t.depth-d)
		t.data[d-1] = make([]T, w)
		t.delayedData[d-1] = make([]T, w)
		t.dirty[d-1] = make([]bool, w)
	}

	return t
}

// Segment Tree end

func main() {
	N = parseInt(readLine())
	for i := 0; i < N; i++ {
		fields := readFields()
		blocks[i].w = parseInt(fields[0])
		blocks[i].s = parseInt(fields[1])
		blocks[i].v = parseInt64(fields[2])
	}

	sort.Sort(SortableBlocks(blocks[:N]))

	// fmt.Println(blocks[:N])

	t := make([]int64, 100001)

	for i := 0; i < N; i++ {
		b := blocks[i]
		for j := b.s; j >= 0; j-- {
			t[j+b.w] = max64(t[j+b.w], t[j]+b.v)
		}
		// fmt.Println(t[:10])
	}

	var max int64
	for _, v := range t {
		if v > max {
			max = v
		}
	}
	fmt.Println(max)
}


Measured memory_bytes: 896