HackerRank: Electronics Shop Challenge from Hacker Rank -

Monica wants to buy a keyboard and a USB drive from her favorite electronics store. The store has several models of each. Monica wants to spend as much as possible for the items, given her budget.
Given the price lists for the store's keyboards and USB drives, and Monica's budget, find and print the amount of money Monica will spend. If she doesn't have enough money to both a keyboard and a USB drive, print -1 instead. She will buy only the two required items.

For example - with a budget of 10, two keyboards costing 3,1 &amp; finally three drives available costing 5,2,8, the answer should be 9 as she is only able to purchase the keyboard for 3 and a drive for 5.
I've attempted to solve this logically and with good performance in mind. I'm not sure if I should be happy with my solution. I would appreciate any feedback.
My solution (which works) or my GitHub repo -
using System;
using System.Collections.Generic;
using System.Linq;

namespace ElectronicsShop
{
    class Program
    {
        static void Main(string[] args)
        {

            Console.WriteLine(GetMoneySpent(new int[] { 3, 1 }, new int[] { 5, 2, 8 }, 10));
            Console.WriteLine(GetMoneySpent(new int[] { 5}, new int[] { 4 }, 5));
            Console.ReadLine();
        }

        static int GetMoneySpent(int[] keyboards, int[] drives, int budget)
        {
            if (budget == 0)
                return -1;

            // sort the two arrays so the highest values are at the front
            keyboards = SortArrayDescending(keyboards);
            drives = SortArrayDescending(drives);

            // delete any that are over our budget
            var affordableKeyboards = GetAffordableItems(keyboards, budget);
            var affordableDrives = GetAffordableItems(drives, budget);

            // make a list to contain the combined totals
            var combinedTotals = new List&lt;int&gt;();

            foreach (var keyboard in keyboards)
            {
                foreach (var drive in drives)
                {
                    combinedTotals.Add(keyboard + drive);
                }
            }

            // sort the list &amp; delete anything over budget
            combinedTotals.Sort();
            combinedTotals.Reverse();
            combinedTotals.RemoveAll(n =&gt; n &gt; budget);

            return combinedTotals.Count == 0 ? -1 : combinedTotals[0];
        }

        static int[] SortArrayDescending(int[] array)
        {
            Array.Sort(array);
            Array.Reverse(array);

            return array;
        }

        static int[] GetAffordableItems(int[] array, int budget)
        {
            return array.Where(n =&gt; n &lt; budget).ToArray();
        }
    }
}
early pruning
This is very nice:
        // delete any that are over our budget

Doing it before sorting can slightly speed the sorting operation.
I say slightly because &quot;items over budget&quot; is determined by the input,
and it will be some fraction f of an input item category,
so the savings is O(f * n * log n).
early discard
This is a bigger deal.
        // sort the list &amp; delete anything over budget

For k keyboards and d drives, the sort does O(k * d * log k * d) work.
Discarding within this loop would be an even bigger win.
consistent idiom
It was a little odd that you used
        combinedTotals.RemoveAll(n =&gt; n &gt; budget);

and
        array.Where(n =&gt; n &lt; budget).ToArray();

to accomplish the same thing.
There's no speed difference but consider phrasing the same thing in the same way.
reversing
If you pass into Sort something that implements the IComparer interface,
you can change the comparison order and thus skip the Reverse step entirely.
arithmetic
Arbitrarily choose one of the categories as the driving category, perhaps keyboard.
Sort the drive prices, while leaving the keyboards in arbitrary order.
Note the min drive price, and use that along with budget for immediately pruning infeasible keyboards.
Loop over all surviving keyboards.
Target price is budget - kb_price.
Do binary search over drives for the target,
finding largest feasible drive,
and use that to update &quot;best combo so far&quot;.
No need to sort them, you need only retain the &quot;best&quot; one.