The problem with dynamic memory allocation is just that it quickly becomes non-deterministic. First, in contrast to static allocation, dynamic memory allocation is not checked at compile/link time, so you generally will have to manually estimate/analyze how much memory in total will be dynamically allocated at runtime. Then, at runtime, allocations/de-allocations may be unpredictably slow and, much worse, they can easily cause fragmentation of free memory which is often an unrecoverable condition once it has progressed somewhat. Memory fragmentation is really bad because it will cause out-of-memory errors although, in total, there's enough free memory, and that's why it's almost impossible to predict. (It depends on the algorithm(s) the memory allocator implements and the size and sequence of allocations and de-allocations. Especially the latter may be impossible to predict if the system uses different allocations/de-allocations in response to external stimuli.)
is it really better to allocate 1000 values and end up using 30 values
Often it's not better.
It is a common error and cause of many bugs and/or vulnerabilities when people just assume an upper bound on some data which just is not specified.
Example: Say my device provides some kind of serial terminal functionality, i.e. some kind of user can send certain commands. A common, simple approach is to read characters into a buffer until e.g. a CR or LF is encountered. The developer may go about and check that the longest valid command is 30 characters long and use that to set the size of his buffer to, say, 30*2 characters, to provide "enough" of safety margin in case he overlooked some possible lengthy command arguments.
However, setting a fixed limit on a buffer and assuming that it will not overflow "because" is just not safe.
The point here is that the majority of buffers and the like are either of a small, fixed (worst-case) size because they will only hold a specific data structure, or are not bounded at all because data comes in from some untrusted/uncontrolled source.
Thus, coding purely based on an estimation of a 'reasonable' worst-case is not enough.
The only (safe) way to handle arbitrarily large data with non-infinite RAM is to process data in parts. Receive a buffer's worth of data, process as much of it as is possible, then receive more data until done. Of course, parsing a full command line from a single contiguous block of memory is much easier than parsing and piecing together multiple parts, storing intermediate states in between, but "easy" is just not the same as "robust" in most cases.
Don't (statically) allocate "probably enough" memory. Prepare for your buffer to be definitely too small at some point and process that case gracefully. At the very least, detect and reject input that would overflow the buffer. If you can, try and handle arbitrary input sizes constructively.