← Home
package main

import (
	"bufio"
	"fmt"
	"math"
	"math/bits"
	"os"
	"sort"
)

const EPS = 1e-9

type Opt struct {
	w int
	x int
}

type LPSolver struct {
	m, n int
	B, N []int
	D    [][]float64
}

func NewLPSolver(A [][]float64, b, c []float64) *LPSolver {
	m := len(b)
	n := len(c)
	D := make([][]float64, m+2)
	for i := range D {
		D[i] = make([]float64, n+2)
	}
	B := make([]int, m)
	N := make([]int, n+1)
	for i := 0; i < m; i++ {
		for j := 0; j < n; j++ {
			D[i][j] = A[i][j]
		}
	}
	for i := 0; i < m; i++ {
		B[i] = n + i
		D[i][n] = -1
		D[i][n+1] = b[i]
	}
	for j := 0; j < n; j++ {
		N[j] = j
		D[m][j] = -c[j]
	}
	N[n] = -1
	D[m+1][n] = 1
	return &LPSolver{m: m, n: n, B: B, N: N, D: D}
}

func (s *LPSolver) Pivot(r, c int) {
	inv := 1.0 / s.D[r][c]
	for i := 0; i < s.m+2; i++ {
		if i == r {
			continue
		}
		for j := 0; j < s.n+2; j++ {
			if j == c {
				continue
			}
			s.D[i][j] -= s.D[r][j] * s.D[i][c] * inv
		}
	}
	for j := 0; j < s.n+2; j++ {
		if j != c {
			s.D[r][j] *= inv
		}
	}
	for i := 0; i < s.m+2; i++ {
		if i != r {
			s.D[i][c] *= -inv
		}
	}
	s.D[r][c] = inv
	s.B[r], s.N[c] = s.N[c], s.B[r]
}

func (s *LPSolver) Simplex(phase int) bool {
	x := s.m
	if phase == 2 {
		x = s.m + 1
	}
	for {
		c := -1
		for j := 0; j <= s.n; j++ {
			if phase == 2 && s.N[j] == -1 {
				continue
			}
			if c == -1 || s.D[x][j] < s.D[x][c]-EPS || (math.Abs(s.D[x][j]-s.D[x][c]) <= EPS && s.N[j] < s.N[c]) {
				c = j
			}
		}
		if s.D[x][c] >= -EPS {
			return true
		}
		r := -1
		for i := 0; i < s.m; i++ {
			if s.D[i][c] <= EPS {
				continue
			}
			if r == -1 {
				r = i
			} else {
				lhs := s.D[i][s.n+1] / s.D[i][c]
				rhs := s.D[r][s.n+1] / s.D[r][c]
				if lhs < rhs-EPS || (math.Abs(lhs-rhs) <= EPS && s.B[i] < s.B[r]) {
					r = i
				}
			}
		}
		if r == -1 {
			return false
		}
		s.Pivot(r, c)
	}
}

func (s *LPSolver) Solve() (feasible bool, bounded bool, val float64, x []float64) {
	r := 0
	for i := 1; i < s.m; i++ {
		if s.D[i][s.n+1] < s.D[r][s.n+1] {
			r = i
		}
	}
	if s.m > 0 && s.D[r][s.n+1] < -EPS {
		s.Pivot(r, s.n)
		if !s.Simplex(2) || s.D[s.m+1][s.n+1] < -EPS {
			return false, false, 0, nil
		}
		if s.D[s.m+1][s.n+1] > EPS {
			return false, false, 0, nil
		}
		if s.B[r] == -1 {
			c := 0
			for j := 1; j <= s.n; j++ {
				if s.D[r][j] < s.D[r][c]-EPS || (math.Abs(s.D[r][j]-s.D[r][c]) <= EPS && s.N[j] < s.N[c]) {
					c = j
				}
			}
			s.Pivot(r, c)
		}
	}
	if !s.Simplex(1) {
		return true, false, math.Inf(1), nil
	}
	x = make([]float64, s.n)
	for i := 0; i < s.m; i++ {
		if s.B[i] >= 0 && s.B[i] < s.n {
			x[s.B[i]] = s.D[i][s.n+1]
		}
	}
	return true, true, s.D[s.m][s.n+1], x
}

type IntSolver struct {
	baseA    [][]float64
	baseB    []float64
	c        []float64
	nvars    int
	varGroup []int
	varMask  []int
	groupCnt []int
	resCap   [3]int
	best     int
}

func (s *IntSolver) buildAB(lb, ub []int) ([][]float64, []float64) {
	rows := make([][]float64, 0, len(s.baseA)+2*s.nvars)
	b := make([]float64, 0, len(s.baseB)+2*s.nvars)
	rows = append(rows, s.baseA...)
	b = append(b, s.baseB...)
	for i := 0; i < s.nvars; i++ {
		if lb[i] > 0 {
			row := make([]float64, s.nvars)
			row[i] = -1
			rows = append(rows, row)
			b = append(b, -float64(lb[i]))
		}
		if ub[i] < s.groupCnt[s.varGroup[i]] {
			row := make([]float64, s.nvars)
			row[i] = 1
			rows = append(rows, row)
			b = append(b, float64(ub[i]))
		}
	}
	return rows, b
}

func (s *IntSolver) greedy(sol []float64, lb, ub []int) int {
	cur := make([]int, s.nvars)
	usedGroup := make([]int, len(s.groupCnt))
	usedCap := [3]int{}
	profit := 0
	for i := 0; i < s.nvars; i++ {
		v := int(math.Floor(sol[i] + 1e-8))
		if v < lb[i] {
			v = lb[i]
		}
		if v > ub[i] {
			v = ub[i]
		}
		cur[i] = v
		profit += v
		g := s.varGroup[i]
		usedGroup[g] += v
		mask := s.varMask[i]
		for j := 0; j < 3; j++ {
			if (mask>>j)&1 == 1 {
				usedCap[j] += v
			}
		}
	}
	remGroup := make([]int, len(s.groupCnt))
	for i := range s.groupCnt {
		remGroup[i] = s.groupCnt[i] - usedGroup[i]
		if remGroup[i] < 0 {
			return profit
		}
	}
	remCap := [3]int{s.resCap[0] - usedCap[0], s.resCap[1] - usedCap[1], s.resCap[2] - usedCap[2]}
	if remCap[0] < 0 || remCap[1] < 0 || remCap[2] < 0 {
		return profit
	}
	idx := make([]int, s.nvars)
	for i := 0; i < s.nvars; i++ {
		idx[i] = i
	}
	sort.Slice(idx, func(a, b int) bool {
		ia, ib := idx[a], idx[b]
		sa, sb := bits.OnesCount(uint(s.varMask[ia])), bits.OnesCount(uint(s.varMask[ib]))
		if sa != sb {
			return sa < sb
		}
		fa := sol[ia] - float64(cur[ia])
		fb := sol[ib] - float64(cur[ib])
		if math.Abs(fa-fb) > 1e-9 {
			return fa > fb
		}
		return ia < ib
	})
	for _, i := range idx {
		g := s.varGroup[i]
		add := ub[i] - cur[i]
		if add > remGroup[g] {
			add = remGroup[g]
		}
		mask := s.varMask[i]
		for j := 0; j < 3; j++ {
			if (mask>>j)&1 == 1 && add > remCap[j] {
				add = remCap[j]
			}
		}
		if add <= 0 {
			continue
		}
		cur[i] += add
		profit += add
		remGroup[g] -= add
		for j := 0; j < 3; j++ {
			if (mask>>j)&1 == 1 {
				remCap[j] -= add
			}
		}
	}
	return profit
}

func (s *IntSolver) dfs(lb, ub []int) {
	A, b := s.buildAB(lb, ub)
	lp := NewLPSolver(A, b, s.c)
	feasible, bounded, val, sol := lp.Solve()
	if !feasible || !bounded {
		return
	}
	ubv := int(math.Floor(val + 1e-7))
	if ubv <= s.best {
		return
	}
	lbv := s.greedy(sol, lb, ub)
	if lbv > s.best {
		s.best = lbv
	}
	if ubv <= s.best {
		return
	}
	sel := -1
	bestFrac := 0.0
	for i := 0; i < s.nvars; i++ {
		v := sol[i]
		f := math.Abs(v - math.Round(v))
		if f > 1e-7 && f > bestFrac {
			bestFrac = f
			sel = i
		}
	}
	if sel == -1 {
		if ubv > s.best {
			s.best = ubv
		}
		return
	}
	v := sol[sel]
	fl := int(math.Floor(v + 1e-8))
	ce := int(math.Ceil(v - 1e-8))
	if ce <= ub[sel] {
		lb2 := append([]int(nil), lb...)
		if ce > lb2[sel] {
			lb2[sel] = ce
		}
		s.dfs(lb2, ub)
	}
	if fl >= lb[sel] {
		ub2 := append([]int(nil), ub...)
		if fl < ub2[sel] {
			ub2[sel] = fl
		}
		s.dfs(lb, ub2)
	}
}

func (s *IntSolver) Solve() int {
	lb := make([]int, s.nvars)
	ub := make([]int, s.nvars)
	for i := 0; i < s.nvars; i++ {
		ub[i] = s.groupCnt[s.varGroup[i]]
	}
	s.best = 0
	s.dfs(lb, ub)
	return s.best
}

func feasibleOptions(n, total, hackable int) []Opt {
	minSolved := total - hackable
	res := []Opt{}
	for lv := 1; lv <= 6; lv++ {
		var L, R int
		if lv < 6 {
			L = n/(1<<lv) + 1
			R = n / (1 << (lv - 1))
		} else {
			L = 0
			R = n / 32
		}
		lo := minSolved
		if L > lo {
			lo = L
		}
		hi := total
		if R < hi {
			hi = R
		}
		if lo <= hi {
			x := total - lo
			res = append(res, Opt{w: 2 * lv, x: x})
		}
	}
	return res
}

func main() {
	in := bufio.NewReaderSize(os.Stdin, 1<<20)
	var n int
	fmt.Fscan(in, &n)

	t := make([][3]int, n)
	base := make([][3]int, n)
	hackable := make([][3]bool, n)
	totalSolved := [3]int{}
	totalHackable := [3]int{}

	for i := 0; i < n; i++ {
		fmt.Fscan(in, &t[i][0], &t[i][1], &t[i][2])
		for j := 0; j < 3; j++ {
			x := t[i][j]
			if x != 0 {
				totalSolved[j]++
				if x < 0 {
					totalHackable[j]++
					hackable[i][j] = true
					x = -x
				}
				base[i][j] = 250 - x
			}
		}
	}

	opts := [3][]Opt{}
	for j := 0; j < 3; j++ {
		opts[j] = feasibleOptions(n, totalSolved[j], totalHackable[j])
	}

	bestBeaten := 0

	for _, o0 := range opts[0] {
		for _, o1 := range opts[1] {
			for _, o2 := range opts[2] {
				w := [3]int{o0.w, o1.w, o2.w}
				cap := [3]int{o0.x, o1.x, o2.x}
				your := 100 * (cap[0] + cap[1] + cap[2])
				for j := 0; j < 3; j++ {
					if t[0][j] > 0 {
						your += w[j] * base[0][j]
					}
				}

				alreadyBelow := 0
				groupMap := map[int]int{}

				for i := 1; i < n; i++ {
					full := 0
					hmask := 0
					rv := [3]int{}
					for j := 0; j < 3; j++ {
						if base[i][j] == 0 {
							continue
						}
						v := w[j] * base[i][j]
						full += v
						if hackable[i][j] {
							hmask |= 1 << j
							rv[j] = v
						}
					}
					d := full - your
					if d <= 0 {
						alreadyBelow++
						continue
					}
					if hmask == 0 {
						continue
					}
					suff := [8]bool{}
					for s := 1; s < 8; s++ {
						if s&^hmask != 0 {
							continue
						}
						sum := 0
						for j := 0; j < 3; j++ {
							if (s>>j)&1 == 1 {
								sum += rv[j]
							}
						}
						if sum >= d {
							suff[s] = true
						}
					}
					key := 0
					for s := 1; s < 8; s++ {
						if !suff[s] {
							continue
						}
						minimal := true
						for sub := (s - 1) & s; sub > 0; sub = (sub - 1) & s {
							if suff[sub] {
								minimal = false
								break
							}
						}
						if minimal {
							key |= 1 << (s - 1)
						}
					}
					if key != 0 {
						groupMap[key]++
					}
				}

				neutralized := 0
				if len(groupMap) > 0 {
					groupCnt := make([]int, 0, len(groupMap))
					keys := make([]int, 0, len(groupMap))
					for k, v := range groupMap {
						keys = append(keys, k)
						groupCnt = append(groupCnt, v)
					}

					varGroup := []int{}
					varMask := []int{}
					for g, key := range keys {
						for mask := 1; mask < 8; mask++ {
							if ((key >> (mask - 1)) & 1) == 1 {
								varGroup = append(varGroup, g)
								varMask = append(varMask, mask)
							}
						}
					}

					nvars := len(varGroup)
					rows := len(groupCnt) + 3
					A := make([][]float64, rows)
					for i := 0; i < rows; i++ {
						A[i] = make([]float64, nvars)
					}
					bv := make([]float64, rows)
					for i, cnt := range groupCnt {
						bv[i] = float64(cnt)
					}
					for j := 0; j < 3; j++ {
						bv[len(groupCnt)+j] = float64(cap[j])
					}
					for i := 0; i < nvars; i++ {
						g := varGroup[i]
						mask := varMask[i]
						A[g][i] = 1
						for j := 0; j < 3; j++ {
							if (mask>>j)&1 == 1 {
								A[len(groupCnt)+j][i] = 1
							}
						}
					}
					c := make([]float64, nvars)
					for i := range c {
						c[i] = 1
					}

					solver := IntSolver{
						baseA:    A,
						baseB:    bv,
						c:        c,
						nvars:    nvars,
						varGroup: varGroup,
						varMask:  varMask,
						groupCnt: groupCnt,
						resCap:   cap,
					}
					neutralized = solver.Solve()
				}

				beaten := alreadyBelow + neutralized
				if beaten > bestBeaten {
					bestBeaten = beaten
				}
			}
		}
	}

	fmt.Println(n - bestBeaten)
}

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