Reputation: 103525
Here is my problem. Imagine I am buying 3 different items, and I have up to 5 coupons. The coupons are interchangeable, but worth different amounts when used on different items.
Here is the matrix which gives the result of spending different numbers of coupons on different items:
coupons: 1 2 3 4 5
item 1 $10 off $15 off
item 2 $5 off $15 off $25 off $35 off
item 3 $2 off
I have manually worked out the best actions for this example:
However, I need to develop a general algorithm which will handle different matrices and any number of items and coupons.
I suspect I will need to iterate through every possible combination to find the best price for n coupons. Does anyone here have any ideas?
Upvotes: 7
Views: 4616
Reputation: 8744
This seems like a good candidate for dynamic programming:
//int[,] discountTable = new int[NumItems][NumCoupons+1]
// bestDiscount[i][c] means the best discount if you can spend c coupons on items 0..i
int[,] bestDiscount = new int[NumItems][NumCoupons+1];
// the best discount for a set of one item is just use the all of the coupons on it
for (int c=1; c<=MaxNumCoupons; c++)
bestDiscount[0, c] = discountTable[0, c];
// the best discount for [i, c] is spending x coupons on items 0..i-1, and c-x coupons on item i
for (int i=1; i<NumItems; i++)
for (int c=1; c<=NumCoupons; c++)
for (int x=0; x<c; x++)
bestDiscount[i, c] = Math.Max(bestDiscount[i, c], bestDiscount[i-1, x] + discountTable[i, c-x]);
At the end of this, the best discount will be the highest value of bestDiscount[NumItems][x]. To rebuild the tree, follow the graph backwards:
edit to add algorithm:
//int couponsLeft;
for (int i=NumItems-1; i>=0; i++)
{
int bestSpend = 0;
for (int c=1; c<=couponsLeft; c++)
if (bestDiscount[i, couponsLeft - c] > bestDiscount[i, couponsLeft - bestSpend])
bestSpend = c;
if (i == NumItems - 1)
Console.WriteLine("Had {0} coupons left over", bestSpend);
else
Console.WriteLine("Spent {0} coupons on item {1}", bestSpend, i+1);
couponsLeft -= bestSpend;
}
Console.WriteLine("Spent {0} coupons on item 0", couponsLeft);
Storing the graph in your data structure is also a good way, but that was the way I had thought of.
Upvotes: 5
Reputation: 19889
This can be written as a linear programming problem. For most 'typical' problems, the simplex method is a fast, relatively simple way to solve such problems, or there are open source LP solvers available.
For your example:
Let 0 <= xi <= 1
Assume that if I spend two coupons on item 1, then both x1 and x2 are one. This implies the constraint
With similar constraints for the other items, e.g.,
The amount saved is
If you want to find the most money saved with a fixed number of coupons, then you want to minimize Saved subject to the constraints above and the additional constraint:
This works for any matrix and number of items. Changing notation (and feeling sad that I can't do subscripts), let 0 <= y_ij <= 1 be one if j coupons are spent on item number i. The we have the constraints
If the amount saved from spending j coupons on item i is M_ij, where we define M_i0 = 0, then maximize
subject to the above constraints and
(The italics formatting doesn't seem to be working here)
Upvotes: 2
Reputation: 2207
I think dynamic programming should do this. Basically, you keep track of an array A[n, c] whose values mean the optimal discount while buying the n first items having spent c coupons. The values for a[n, 0] should be 0 for all values of n, so that is a good start. Also, A[0, c] is 0 for all c.
When you evaluate A[n,c], you loop over all discount offers for item n, and add the discount for that particular offer to A[n-1,c-p] where p is the price in coupons for this particular discount. A[n-1, c-p] must of course be calculated (in the same way) prior to this. Keep the best combination and store in the array.
A recursive implementation would probably give the cleanest implementation. In that case, you should find the answer in A[N,C] where N is the total number of items and C is the total number of available coupons.
Upvotes: 3
Reputation: 91482
I suspect some kind of sorted list for memoizing each coupon-count can help here.
for example, if you have 4 coupons, the optimum is possibly:
this answer is pretty vague.. I need to put more thought into it.
Upvotes: 0
Reputation: 43845
It's the Knapsack problem, or rather a variation of it. Doing some research on algorithms related to this problem will point you in the best direction.
Upvotes: 4
Reputation: 38356
This problem is similar in concept to the Traveling Salesman problem where O(n!) is the best for finding the optimal solution. There are several shortcuts that can be taken but they required lots and lots of time to discover, which I doubt you have.
Checking each possible combination is going to be the best use of your time, assuming we are dealing with small numbers of coupons. Make the client wait a bit instead of you spending years on it.
Upvotes: -2