Find the optimal bills combination to pay for a specific value - c#

I am working on game with payment system with the following requirements:
1- Suppose the money bills in the game are: 10,5,4,3,2,1
2- AI needs to choose the least number of bills needed to cover the exact amount,i.e if the required to pay is 8 and AI has (4,4,3,3,2)...He can choose (4,4) but not (3,3,2)
3- In case AI can't make the exact amount using the bills he has, he should choose the combination such that it gives the amount with the least difference value, i.e. if the required amount to pay is 7 and the AI has the following bills ( 10,5,4,4), he choses (4,4) which gives the player 1 more above the needed amount.
Below is my code
//sortedValues is a list containing my bills in descending order
//ChosenCardsToPay is a list for the bills I choose to pay with
public void PreparePayment(int neededAmount)
{
int remainingAmount = neededAmount;
int chosenAmount;
while (remainingAmount > 0)
{
chosenAmount = 0;
foreach (int moneyValue in sortedValues )
{
if (moneyValue <= remainingAmount)
{ chosenCardsToPay.Add (moneyValue); //Add Bill Value to my candidate list
remainingAmount = remainingAmount - moneyValue;
chosenAmount = moneyValue;
break;
}
}
if (chosenAmount != 0)
sortedValues.Remove (moneyValue);//Remove Chosen Bill from Initial List
else //If all bill values are greater than remaining amount, i choose the bill with smallest value and add to the candidate list
{
chosenAmount = sortedValues.Last();
sortedValues.Remove(chosenAmount);
chosenCardsToPay.Add (chosenAmount);
remainingAmount = remainingAmount - moneyValue;
}
}
}
It works fine most of the times, but take this case: Required amount is 4 and AI has (3,2,2) as bills. Using the above algorithm, AI chooses (3,2) where he optimal answer is ( 2,2).
May someone direct me to the right thinking about this problem? Thanks!

Here's a recursive solution I came up with. The idea is to keep track of "overages" and return immediately when you find an exact match. If no exact match is found you just sort the overages by how much they are over then by how many bills were required and take the first one. In order to get the fewest bills on exact matches makes sure that bills is sorted in descending order. Also this will return an empty sequence if there isn't a way to cover the amount with the given set of bills.
public static IEnumerable<int> CoverAmount(
int amount, List<int> bills, HashSet<int> used = null)
{
if (used == null)
used = new HashSet<int>();
if (amount <= 0)
return Enumerable.Empty<int>();
var overages = new List<Tuple<List<int>, int>>();
for(int index = 0; index < bills.Count; index++)
{
var bill = bills[index];
if (used.Contains(index))
continue;
if (bill > amount)
{
overages.Add(Tuple.Create(new List<int> { bill }, bill - amount));
}
else if (bill == amount)
{
return Enumerable.Repeat(bill, 1);
}
else
{
used.Add(index);
var bestSub = CoverAmount(amount - bill, bills, used).ToList();
used.Remove(index);
bestSub.Add(bill);
var sum = bestSub.Sum();
if (sum == amount)
{
return bestSub;
}
if (sum > amount)
{
overages.Add(Tuple.Create(bestSub, sum - amount));
}
}
}
return overages
.OrderBy(t => t.Item2)
.ThenBy(t => t.Item1.Count)
.FirstOrDefault()?.Item1 ?? Enumerable.Empty<int>();
// OR this if you are not using C# 6
// var bestOverage = overages
// .OrderBy(t => t.Item2)
// .ThenBy(t => t.Item1.Count)
// .FirstOrDefault();
// return bestOverage == null ? Enumerable.Empty<int>() : bestOverage.Item1;
}
The following code
Console.WriteLine(string.Join(", ", CoverAmount(8, new List<int> { 4, 4, 3, 3, 2 })));
Console.WriteLine(string.Join(", ", CoverAmount(7, new List<int> { 10, 5, 4, 4 })));
Console.WriteLine(string.Join(", ", CoverAmount(4, new List<int> { 3, 2, 2 })));
Console.WriteLine(string.Join(", ", CoverAmount(10, new List<int> { 11, 6, 5 })));
will produce this output
4, 4
4, 4
2, 2
11

Related

How to find all possible values of 100 dollar bill [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 months ago.
Improve this question
banknotes provided = 10 Dollar, 50 Dollar, 100 Dollar
Expected Result for 100 dollar bill with above bank notes.
I want to show the denomination exactly like below
5 x 10 + 1x 50
2 x 50
1 x 100
10 x 10
As you can see all the banknotes denomination total to 100 above
Let's split the problem in two:
Solve the exchange problem of total given notes (in descending order), starting from nominal at index.
Provide the solution in required format
Code:
private static IEnumerable<int[]> Solutions(int total, int[] notes, int index) {
int value = notes[index];
if (index == notes.Length - 1) {
if (total % value == 0)
yield return new int[] { total / value };
}
else
for (int i = total / value; i >= 0; --i)
foreach (int[] rest in Solutions(total - value * i, notes, index + 1))
yield return rest.Prepend(i).ToArray();
}
private static IEnumerable<string> Solve(int total, int[] notes) {
var items = notes.OrderByDescending(item => item).ToArray();
foreach (int[] solution in Solutions(total, items, 0))
yield return string.Join(" + ", solution
.Zip(items, (count, note) => (count, note))
.Where(pair => pair.count > 0)
.Select(pair => $"{pair.count} x {pair.note}"));
}
Demo:
int total = 170;
int[] notes = new int[] {10, 50, 100};
Console.Write(string.Join(Environment.NewLine, Solve(total, notes)));
Output:
1 x 100 + 1 x 50 + 2 x 10
1 x 100 + 7 x 10
3 x 50 + 2 x 10
2 x 50 + 7 x 10
1 x 50 + 12 x 10
17 x 10
Fiddle
Here is another (way less compact and cool than Dmitry's) solution, that uses recursion as you said. Basically I create a tree for each possible sum of the available banknotes that starts with the given amount at the root. For example, for your given amount of 100, and the given notes 10, 50, 100, I create the following pictured trees (and more), where the leaf values represent the individual banknotes that make up the total amount.
The leaf values then get transformed to your output, and possible duplicates (since different trees can be created from the same bank notes) are not printed out.
The following input:
int[] availableBanknotes = { 10, 50, 100 };
int amount = 100;
SolveRecursive(amount, availableBanknotes);
prints out:
10 x 10
1 x 50 + 5 x 10
2 x 50
1 x 100
The following input:
int[] availableBanknotes = { 10, 50, 100 };
int amount = 170;
SolveRecursive(amount, availableBanknotes);
prints out:
17 x 10
1 x 50 + 12 x 10
2 x 50 + 7 x 10
1 x 100 + 7 x 10
3 x 50 + 2 x 10
1 x 100 + 1 x 50 + 2 x 10
Here is the code:
private static List<string> Solutions = new List<string>();
private static void SolveRecursive(int amount, int[] availableBanknotes)
{
List<int> denomination = new List<int>();
for (int i = 0; i < availableBanknotes.Length; i++)
{
if (amount >= availableBanknotes[i])
{
ProvidedBanknotes(amount, availableBanknotes[i], availableBanknotes, new List<int>(denomination));
}
}
Solutions = new List<string>();
}
private static void ProvidedBanknotes(int amount, int currentBankNote, int[] availableBanknotes, List<int> denomination)
{
amount = amount - currentBankNote;
denomination.Add(currentBankNote);
if (amount == 0)
{
PrintSolution(denomination, availableBanknotes);
return;
}
for (int i = 0; i < availableBanknotes.Length; i++)
{
if(amount >= availableBanknotes[i])
{
ProvidedBanknotes(amount, availableBanknotes[i], availableBanknotes, new List<int>(denomination));
}
}
}
private static void PrintSolution(List<int> denomination, int[] availableBanknotes)
{
string solution = "";
for(int i = availableBanknotes.Length - 1; i >= 0; i--)
{
int currentVal = availableBanknotes[i];
int count = denomination.Where(temp => temp.Equals(currentVal))
.Select(temp => temp)
.Count();
if (count != 0)
{
if(solution.Equals(""))
{
solution = $"{count} x {currentVal}";
}
else
{
solution = solution + $" + {count} x {currentVal}";
}
}
}
if(!Solutions.Contains(solution))
{
Solutions.Add(solution);
Console.WriteLine(solution);
}
}
Given
var notes = new[] { 10, 50, 100 };
var total = 100;
You can use a LINQ Extension Method to create all possible combinations of notes and filter them to just combinations that could be used to create the total. I sort the notes in the combination descending so that the final answer will be output in descending order:
var possibleCombos = notes.Where(n => n <= total)
.AllCombinations()
.Select(c => c.OrderByDescending(n => n).ToList())
.Where(c => c.Sum() <= total && c.Aggregate(total-c.Sum(), (r, n) => r % n) == 0)
.OrderBy(c => c.Last());
While it doesn't apply to the example, the Aggregate expression ensures that impossible combinations aren't selected, such as trying to make { 50, 20 } yield 100.
For each possible combination, you can start with the total and then take away one of each note to ensure at least one of each note is in the answer. It then computes the number of additional notes needed for each denomination, starting with the largest. For each combination it outputs the number of each note in the format requested:
foreach (var combo in possibleCombos) {
var numberOfNotes = Enumerable.Repeat(1, combo.Count).ToList(); // one of each denomination
var remainder = total - combo.Sum(); // start by taking away one of each denomination
for (int j1 = 0; j1 < combo.Count; ++j1) {
numberOfNotes[j1] += remainder / combo[j1]; // take away all possible of each denomination
remainder = remainder % combo[j1];
}
Console.WriteLine(String.Join(" + ", numberOfNotes.Select((numberOfNote, j1) => $"{numberOfNote} x {combo[j1]}")));
}
The extension method is defined as:
public static class IEnumerableExt {
public static IEnumerable<IEnumerable<T>> AllCombinations<T>(this IEnumerable<T> start) {
IEnumerable<IEnumerable<T>> HelperCombinations(IEnumerable<T> items) {
if (items.IsEmpty())
yield return items;
else {
var head = items.First();
var tail = items.Skip(1);
foreach (var sequence in HelperCombinations(tail)) {
yield return sequence; // Without first
yield return sequence.Prepend(head);
}
}
}
return HelperCombinations(start).Skip(1); // don't return the empty set
}
}
NOTE: This only produces one possible solution for each combination of bills, the one that uses the maximum possible number of the largest denomination.
Here's my take on this:
public IEnumerable<List<int>> GetDenominations(List<int> denominations, int value) =>
GetDenominations(new List<int>(), denominations, value);
private IEnumerable<List<int>> GetDenominations(List<int> bills, List<int> denominations, int value)
{
int sum = bills.Sum();
if (sum == value)
{
yield return bills;
}
else if (sum < value && denominations.Any())
{
for (int i = 0; i < denominations.Count; i++)
{
List<int> denominations2 = denominations.Skip(i).ToList();
List<int> bills2 = bills.Append(denominations2.First()).ToList();
foreach (var result in GetDenominations(bills2, denominations2, value))
{
yield return result;
}
}
}
}
That produces all valid combinations of the denominations that add up the value passed.
I get the results like this:
IEnumerable<List<int>> results = GetDenominations(new List<int>() { 10, 50, 100 }, 170);
And formatting is just a little LINQ work:
string output =
String
.Join(
Environment.NewLine,
results
.Reverse()
.Select(xs =>
String
.Join(
" + ",
xs
.OrderByDescending(x => x)
.GroupBy(x => x)
.Select(x => $"{x.Count()} x {x.Key}"))));
That gives me:
1 x 100 + 1 x 50 + 2 x 10
3 x 50 + 2 x 10
1 x 100 + 7 x 10
2 x 50 + 7 x 10
1 x 50 + 12 x 10
17 x 10

How to find the max continuous occurrences of an item in a list<>?

I would like to find the max continuous occurrence of a string in a list.
Let's say I have List where Drivers has Driver.Name, Driver.Nationality, Driver.Car
Now we have a list of races where and I store the winners in a List
I would like to see what was the most time that Ferrari wins in a row, or French drivers win in a row.
Drivers.add{Alex, French, Ferrari}
Drivers.add{Peter, Russian, Ferrari}
Drivers.add{John, USA, Mercedes}
Drivers.add{May, USA, Toyota}
Drivers.add{Hannah, French, Mercedes}
List<Drivers> Winners
Alex,
Peter,
Alex,
John
May,
John,
Hannah
And the results:
Max Ferrari 3
Max USA 3
etc.
Here is the way I tried with Color parameter. "R" is a list of the racers. It works, I am just looking for more elegant way.
foreach(var e in R)
{
if (e.Color == lastColor)
{ Current++;
if (Current > Max) { Max = Current; maxColor = e.Color; }
}
else { lastColor = e.Color; Current = 1; }
}
You should be able to do this in O(n) time by just using an int variable for the 'max run length' and an int variable for the 'current run length' of strings. You can iterate over your list and keep track of your current and max run lengths.
There is no easy way as .NET doesn't have anything for grouping consecutive items.
var Drivers = new[] { new { Name = "Alex" , Team = "Ferrari" },
new { Name = "Peter" , Team = "Ferrari" },
new { Name = "John" , Team = "Mercedes" },
new { Name = "May" , Team = "Toyota" },
new { Name = "Hannah", Team = "Mercedes" } };
var Winners = "Alex Peter Alex John May John Hannah".Split();
var Teams = Winners.Join(Drivers, w => w, d => d.Name, (w, d) => d.Team); // Ferrari, Ferrari, Ferrari, Mercedes, Toyota, Mercedes, Mercedes
var Counts = Teams.Aggregate(new List<Tuple<int, string>>(), (L, t) => { L.Add(
Tuple.Create(L.Any() && L.Last().Item2 == t ? L.Last().Item1 + 1 : 1, t)); return L; }); // (1, Ferrari), (2, Ferrari), (3, Ferrari), (1, Mercedes), (1, Toyota), (1, Mercedes), (2, Mercedes)
var Max = Counts.Max(); // (3, Ferrari)
Custom extension methods like MoreLinq can be used to shorten the Aggregate part:
var Max = Teams.GroupAdjacent(t => t).MaxBy(g => g.Count());
Debug.Print("Max " + Max.Key + " " + Max.Count()); // "Max Ferrari 3"

Finding combinations of list of lists that add up to a target number

I've got an interesting issue I'm trying to solve. My knowledge of Linq is honestly very shallow and I'm pretty certain this is the sort of problem that would be most elegantly solved with a Linq based solution but I've attempted a few things so far with what little knowledge I have to little success.
Here's the skinny: I have a List of decimal Lists and I want to find a combination from the lists adding up to a target decimal using only one element from each list. To clarify:
List<List<decimal>> parentList; // this is the main list I'm drawing from
List<decimal> childList { 1 , 2 , 3 , 4 , 5 }; // each list inside of the main list would look something like this
So if my parentList contains five of the childLists, I need to find a combination that only uses one item each list once. This doesn't mean I can't use the same value twice, if parentList[0] and parentList[1] both contain 3 and I'm adding to 6, {3,3} would be a valid solution. However, if parentList[0] were { 1 , 2 , 3 } and parentList[1] were { 4 }, the only valid solution to add to 6 woudl be {2 , 4}, since the second list doesn't contain 3.
I hope this all makes sense and I'm not asking too much. I don't mind just being oriented in the direction of a solution, a push in the right direction as opposed to the whole answer. Thanks!
As others has already stated, LINQ is not suitable for task like this. Such complex LINQ would not be good both from maintenance and performance perspective.
But I could not stop until I made my inner geek happy! You asked for it...
private static IEnumerable<IEnumerable<decimal>> FindCombinations(IEnumerable<IEnumerable<decimal>> listOfLists, decimal target)
{
return listOfLists.Aggregate(
Enumerable.Repeat(Enumerable.Empty<decimal>(), 1),
(acc, seq) =>
from accseq in acc
from item in seq
select accseq.Concat(new[] {item}))
.Where(x => x.Sum(y => y) == target);
}
And here is console test application:
private static void Main(string[] args)
{
var target = 12;
var listOfLists = new List<List<decimal>>()
{
new List<decimal> { 1, 2, 3 },
new List<decimal> { 3, 4, 5 },
new List<decimal> { 5, 6, 7 },
};
foreach (var combination in FindCombinations(listOfLists, target))
{
Console.WriteLine("{0} = {1}", string.Join(" + ", combination.Select(y => y.ToString(CultureInfo.InvariantCulture))), target);
}
Console.ReadKey();
}
Sounds like something you would solve using recursion and not Linq. Here is an example:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
List<List<decimal>> listOfLists = new List<List<decimal>>()
{
new List<decimal>() { 1, 2, 3 },
new List<decimal>() { 3, 4, 5 },
new List<decimal>() { 5, 6, 7 },
};
PrintAllCombinationsForTargetValue(listOfLists, 12);
}
private static void PrintAllCombinationsForTargetValue(List<List<decimal>> listOfLists, decimal targetValue)
{
Stack<decimal> currentCombination = new Stack<decimal>();
FindNextElement(listOfLists, targetValue, 0, 0, currentCombination);
}
private static void FindNextElement(List<List<decimal>> listOfLists, decimal targetValue, int listIndex, decimal trackingValue, Stack<decimal> currentCombination)
{
List<decimal> currentList = listOfLists[listIndex];
foreach (decimal currentValue in currentList)
{
decimal currentTrackingValue = trackingValue + currentValue;
currentCombination.Push(currentValue);
if (currentTrackingValue < targetValue && listIndex < listOfLists.Count - 1)
{
// There is still la chance that we can get what we want. Let's go to the next list.
FindNextElement(listOfLists, targetValue, listIndex + 1, currentTrackingValue, currentCombination);
}
else if (currentTrackingValue == targetValue && listIndex == listOfLists.Count - 1)
{
// Found a valid combination!
currentCombination.Reverse().ToList().ForEach(element => Console.Write(element + " "));
Console.WriteLine();
}
currentCombination.Pop();
}
}
}
}
You can achieve this with recursion. This will find one combination that sums up to the target, using one value from each list, or null if none exists.
public static List<decimal> CombinationSumMatches(
this IEnumerable<IEnumerable<decimal>> lists,
decimal target)
{
if (lists.Any())
{
var firstList = lists.First();
if (lists.Skip(1).Any())
{
foreach (var num in firstList)
{
var newTarget = target - num;
var subCombination = lists.Skip(1).CombinationSumMatches(newTarget);
if (subCombination != null)
{
subCombination.Insert(0, num);
return subCombination;
}
}
}
else
{
if (firstList.Contains(target))
{
return new List<decimal> { target };
}
}
}
return null;
}
This will first check if there are any lists. If there are then it looks at the first one and sees if there are more. If there are more it goes through each number of the first list and subtracts that value from the target and does a recursive call on the remaining lists. If there is a non null answer it inserts the number and returns. Now if there is only one list then it just checks the list for the target and returns a list with that target value if it finds it. If there are no lists, or only one without the target, or nothing that matches the sub combinations then it will just return null.

Linq order list using list property

If i have a list of football teams, and each team contains a list matches. If each match has a property of goals scored, how can i order the list of football teams so that it is ordered by the most goals scored in the lastest match, followed by the match before and so on?
The number of matches is unknown.
I cant figure out the linq and im not having much luck with investigating dynamic linq
Many thanks
The number of matches will always be the same and theoretically there isnt a maximum although it is reasonable to expect that it will be less than 20. If the number of goals is the same it will use team name in alphabetical order.
Linq doesn't do recursion natively. You may need to define a custom comparer in order to to the recursive search, then pass that to OrderBy. Without seeing the actual structure the pseudo-code would be:
N = 1
while(true)
{
if L has less than N matches
if R has less than matches
return L.Name.CompareTo(R.Name) // order by team name
else
return 1 // R has more matches
if R has less than matches // L has more matches
return -1
compare Nth match of each team
if equal
N = N + 1;
else
return compare result
}
Recursion seems to not be necessary. Here's an iterative approach.
void Main() {
var teams = CreateTeams().ToArray();
int games = teams.Min(t => t.Games.Count);
var ordered = teams.OrderBy(team => 0);
for (int i = games - 1; i >= 0; i--) {
var captured = i; // the value of i will change, so use this capturing variable,
ordered = ordered.ThenByDescending(team => team.Games[captured].Points);
}
ordered = ordered.ThenBy(team => team.Name);
foreach (var team in ordered) {
Console.WriteLine("{0} {1}", team.Name, string.Join(", ", team.Games.Select(game => game.Points)));
}
}
IEnumerable<Team> CreateTeams() {
yield return (new Team("War Donkeys", 1, 2, 3));
yield return (new Team("Fighting Beavers", 2, 2, 3));
yield return (new Team("Angry Potatoes", 2, 1, 3));
yield return (new Team("Wispy Waterfalls", 3, 2, 1));
yield return (new Team("Frisky Felines", 1, 2, 3));
}
class Team {
public string Name { get; set; }
public IList<Game> Games { get; set; }
public Team(string name, params int[] points) {
this.Name = name;
this.Games = points.Select(p => new Game { Points = p }).ToArray();
}
}
class Game {
public int Points { get; set; }
}
The output is
Fighting Beavers 2, 2, 3
Frisky Felines 1, 2, 3
War Donkeys 1, 2, 3
Angry Potatoes 2, 1, 3
Wispy Waterfalls 3, 2, 1

How to search for a number in a list of arrays of numbers based on the first index of each array using LINQ?

I have a list of arrays of numbers. I am searching for the two arrays where my search number falls between the numbers positioned in index 0. Then return the number positioned in index 1 from the second array. (Assume numbers in index 0 are sorted already and there are no duplicates)
My wrong solution for LINQPad:
The value of 'found' should be 3 because 9 falls between 4 & 10 in second and third array. Then I take the second found array and return 3 which is in index 1 of that array.
List<int[]> list = new List<int[]> { new[] { 1, 5 }, new[] { 4, 6 }, new[] { 10, 3} , new[] { 15, 8} };
int searchFor = 9;
int found = list.Where(n => searchFor >= n[0] && searchFor <= n[0]).Select(i => i[1]).FirstOrDefault();
found.Dump(); //should be 3 instead of 0.
Try this :
int found = list.Zip(list.Skip(1), (x, y) => x[0]<=searchFor&&y[0]>=searchFor?y[1]:0).FirstOrDefault(o=>o!=0);
Well my logic is a little different, but get the result you want. I would recommend just using a Dictionary if you are doing key-pair-value stuff like this. It makes things simpler in my opinion and if you have no repeating key's this should work fine.
// Use dictionary instead of array's if just using two int values
var dic = new Dictionary<int, int>();
dic.Add(1, 5);
dic.Add(4, 6);
dic.Add(10, 3);
dic.Add(15, 8);
int searchFor = 9;
// Don't need to find this really
int low = (from l in dic
where l.Key <= searchFor
select l.Key).Max();
// Just need this
int found = (from h in dic
where h.Key >= searchFor
select h.Value).Min();
Console.WriteLine("Low: " + low);
Console.WriteLine("Found: " + found);
How about
var found = list.First(l => l[0] > searchFor)[1];
It should do the trick as I can assume that list is ordered by each first element.
If not, then
var found = list.Orderby(l=>l[0]).First(l => l[0] > searchFor)[1];
should also work.
The expression in the where statement filters for arrays having the first element less or equal and greater or equal than 9. Since it can't be less and greater at the same time it actually filters for all arrays that have 9 as first element.
For the given data this results in an empty sequence. FirstOrDefault therefore returns the default (0 for integers).
You actually have to look for the first element greater or equal than 9:
int[] result = list.FirstOrDefault(arr => arr[0] >= searchFor);
if (result == null)
{
Console.WriteLine("Not found!");
}
else
{
Console.WriteLine(result[1]);
}

Categories

Resources