Brute force shouldn't waste memory, only processing time, I have used it for similar problems. Code doesn't need to look nice or be particularly modular; you know the scope and want to solve it for yourself.
Score each solution, keep track of the best solution so far, and its score. Wastes no memory, only processing power. Even tracking like 16 best solutions as a binary max heap or min heap is low-cost and wastes little CPU or memory, that is, sometimes you want to see a few more results than just the "best", for doing manual choices.
Outer loop can fork bunch of threads which works fine because the problem is CPU heavy and amount of constant input data is very modest likely fitting the per-core L1 caches. Put any parameter with larger dataset in the outer loops.
Consider writing in C or C++ instead of Python for higher performance, nested for loops work fine.
But of course, at some point brute force stops being acceptable, but then you need to understand much more complex problem solving algorithms.