List of numbers and goal -> closes sum - c#

Regarding a question I got from one of my friends I want to ask the best possible solution
The situation is that I have a list of integers for example
2 5 6 8
And I want to get to the integers 17
I can only use each integers ones.
The closest you can get in this case is 16 because no combination leads up to 17.
public class Item
{
public int Weight { get; set; }
public int Value { get; set; }
}
public class Program
{
public static void Main()
{
var items = new[]
{
new Item {Value = 60, Weight = 10},
new Item {Value = 100, Weight = 20},
new Item {Value = 120, Weight = 30},
};
Console.WriteLine(KnapSackRecursive(items, 50));
}
public static int KnapSackRecursive(Item[] items, int capacity)
{
// keep track of the best value seen.
//TODO: Make it a list of numbers
int best = 0;
for (int i = 0; i < items.Length; i++)
{
// This is an array of the other items.
var otherItems = items.Take(i).Concat(items.Skip(i + 1)).ToArray();
// Calculate the best value without using the current item.
int without = KnapSackRecursive(otherItems, capacity);
int with = 0;
// If the current item fits then calculate the best value for
// a capacity less it's weight and with it removed from contention
// and add the current items value to that.
if (items[i].Weight <= capacity)
{
with = KnapSackRecursive(otherItems, capacity - items[i].Weight)
+ items[i].Value;
}
// The current best is the max of the with or without.
int currentBest = Math.Max(without, with);
// determine if the current best is the overall best.
if (currentBest > best)
best = currentBest;
}
return best;
}
}
Edit: It now finds the best possible weight based on the list. It'll result in finding that 20+30 = 50 so it returns 100+120 = 220 I want it to return ("Found best possible combination: 100 + 120 = 220") not just ("220")

Related

Determine if a number can be made with prepicked numbers and times

I have 2 arrays one of the types of numbers that will be used and the 2nd array is how many times that number can be used. I have a letter that determines what kind of method will be used I need to figure out how many times I can use a certain number from an array to determine a letter+number The ‘number’ is what I have to make with all the available numbers I can use. If the number cannot be made I would like to just say number cant be made or anything but allow the program to move on.
Here is what I have
int[] picksToUse = { 100, 50, 20, 10, 5, 1 };
int[] timesToUse = { 10, 10, 10, 10, 10, 10 };
string choice = Console.ReadLine();
string input = "";
if(choice.Length > 2)
{
input = choice.Substring(choice.IndexOf("$") + 1);
}
if(...){
}
else if (choice.Equals("D"))
{
int amt = Convert.ToInt32(input);
// code here to determine if number can be made with above choices
Dispense(amt, timesToUse);
}
Assuming picksToUse and timesToUse are exactly the same as you declared them, here's a way to know if you have enough of everything in stock to "pay up". It's a boolean function, which uses recursion. You would call it with the amount needed as parameter, and it would tell you right there if you have enough of everything.
Private Function HasCashInStock(amount As Integer, Optional index As Integer = 0) As Boolean
Dim billsNeeded As Integer = amount \ picksToUse(index)
If billsNeeded > timesToUse(index) Then
Return False
End If
amount -= picksToUse(index) * billsNeeded
If amount = 0 Then
Return True
End If
Return HasCashInStock(amount, index + 1)
End Function
The \ is an integer division operator (in VB.NET, at least - I'm shamelessly letting you translate this code). If you're not familiar with the integer division operator, well, when you use it with integer it gets rid of the floating numbers.
3 / 2 is not valid on integers, because it would yield 1.5.
3 \ 2 is valid on integers, and will yield 1.
That's all there is to it, really. Oh yeah, and recursion. I like recursion, but others will tell you to avoid it as much as you can. What can I say, I think that a nice recursive function has elegance.
You can also totally copy this function another time, modify it and use it to subtracts from your timesToUse() array once you know for sure that there's enough of everything to pay up.
If HasCashInStock(HereIsTheAmountAsInteger) Then
GivesTheMoney(HereIsTheAmountAsInteger)
End If
Having two functions is not the leanest code but it would be more readable. Have fun!
This is what I used to finish my project.
public static bool Validate(int amount, int[] total, int[] needed)
{
int[] billCount = total;
int[] cash = { 100, 50, 20, 10, 5, 1 };
int total = amount;
bool isValid = true;
for (int i = 0; i < total.Length; i++)
{
if(total >= cash[i])
{
billCount[i] = billCount[i] - needed[i];
}
if(billCount[i] < 0)
{
isValid = false;
break;
}
}
return isValid;
}
I got some working code. I did it with a class. I remember when I couldn't see what good classes were. Now I can't brush my teeth without a class. :-)
I force myself to do these problems to gain a little experience in C#. Now I have 3 things I like about C#.
class ATM
{
public int Denomination { get; set; }
public int Inventory { get; set; }
public ATM(int denom, int inven)
{
Denomination = denom;
Inventory = inven;
}
}
List<int> Bills = new List<int>();
List<ATM> ATMs = new List<ATM>();
private void OP2()
{
int[] picksToUse = { 100, 50, 20, 10, 5, 1 };
foreach (int d in picksToUse )
{
ATM atm = new ATM(d, 10);
ATMs.Add(atm);
}
//string sAmtRequested = Console.ReadLine();
string sAmtRequested = textBox1.Text;
if (int.TryParse(sAmtRequested, out int AmtRequested))
{
int RunningBalance = AmtRequested;
do
{
ATM BillReturn = GetBill(RunningBalance);
if (BillReturn is null)
{
MessageBox.Show("Cannot complete transaction");
return;
}
RunningBalance -= BillReturn.Denomination;
} while (RunningBalance > 0);
}
else
{
MessageBox.Show("Non-numeric request.");
return;
}
foreach (int bill in Bills)
Debug.Print(bill.ToString());
Debug.Print("Remaining Inventory");
foreach (ATM atm in ATMs)
Debug.Print($"For Denomination {atm.Denomination} there are {atm.Inventory} bills remaining");
}
private ATM GetBill(int RequestBalance)
{
var FilteredATMs = from atm in ATMs
where atm.Inventory > 0
orderby atm.Denomination descending
select atm;
foreach (ATM bill in FilteredATMs)
{
if (RequestBalance >= bill.Denomination )
{
bill.Inventory -= 1;
Bills.Add(bill.Denomination);
return bill;
}
}
return null;
}

Adding object item to array giving wrong results

Currently working on a base RPG system, and I've run into an error with a method. The method is meant to add an item (Class within the code) and an optional amount to the players inventory (An Item[] array).
This is the code for the item class:
public class Item
{
public string Name;
public int quantity;
public int maxstack;
public int[] stats = new int[5];
public Item(string name, int Amount = 1, int MaxStack = 10, int ATK = 0,
int DEF = 0, int MAT = 0, int MDF = 0, int SPD = 0)
{
Name = name;
quantity = Amount;
maxstack = MaxStack;
stats[0] = ATK;
stats[1] = DEF;
stats[2] = MAT;
stats[3] = MDF;
stats[4] = SPD;
}
}
The key variables are "quantity" and "maxstack."
Now, the issue comes when adding an item to the players inventory, regarding these variables.
The method, when adding an Item to the inventory, searches for both an empty slot within the inventory and any stacks of the same item using Array.IndexOf();
This is the code for the AddItem() method:
public void AddItem(Item item, int Amount = 1)
{
for (int i = Amount; i > 0; i--)
{
int ItemIndex = Array.IndexOf(inv, item); // Searches for a matching item
int EmptySlot = Array.IndexOf(inv, null); // Searches for an empty slot
ItemCheck:
if (ItemIndex != -1) // ItemIndex will equal -1 if no matching item was found
{
if (inv[ItemIndex].quantity >= inv[ItemIndex].maxstack) // Is the quantity/stack of the found item equal to its maximum stackable value?
{
ItemIndex = Array.IndexOf(inv, item, ItemIndex + 1); // If yes, search for another index.
goto ItemCheck;
} else {
inv[ItemIndex].quantity++; // If the stack hasn't reached its max, increase it by one.
}
}
else
{
inv[EmptySlot] = item; // If no matching item was found, use an empty slot to create a new stack.
inv[EmptySlot].quantity = 1;
}
}
}
Now, lets say I create an item called "stick" and it can only stack a maximum of 3. When running AddItem(stick, 3) and listing the quantity of each stack, the console returns
Stick x1
Stick x1
Can anyone help me? Why is my code turning the stack back into a quantity of 1?
EDIT:
Adding 1, 2 or 3 sticks returns the correct quantity, but only adding more sticks once a stack has reached its max throws the wrong result.
EDIT:
Adding 6 sticks returns 2 stacks with 3 items in each. Adding 7 sticks returns 3 stacks with 1 item in each.
The biggest problem here is you use the item to store the quantity... but the item is used in multiple places in your inventory. So when you change the quantity back to 1, you are changing it for every slot that the item exists in in the inventory.
ie, you don't have copies of the item, you have the same object multiple times.
Many ways you can solve this, but perhaps you should make a new class called a InventorySlot and put the quantity in there.
public class InventorySlot
{
public Item Item {get; set;}
public int Quantity {get; set;}
}
now you players inventory is an array of InventorySlots... like so
given you have something like this in your player...
public InventorySlot[] inv = new InventorySlot[5];
then
public void AddItem(Item item, int amount = 1)
{
var slot = inv.FirstOrDefault(s => s?.Item == item && s.Quantity < item.maxstack);
if (slot != null)
{
slot.Quantity++;
}
else
{
slot = inv.FirstOrDefault(s => s == null);
if (slot != null)
{
slot.Item = item;
slot.Quantity = 1;
}
}
}
I don't understand why would you need to add an item to an array, when the standard library already provides List<T>, which defines methods such as Add, Remove, RemoveAt, Insert and IndexOf.
Example:
List<Item> yourList = new List<Item>(); // Create an empty list
/* Or
* List<Item> youtList = new List<Item>()
* {
* new Item(),
* new Item() // etc
* }
*/
yourList.Add(new Item()); // Add an item
yourList.Insert(0, new Item()); // Insert an item to the 0 index
yourList.Remove(yourItem); // Remove an item by reference
yourList.RemoveAt(0); // Remove an item by its position in the list
Item[] yourArray = yourList.ToArray(); // Create an array with the elements in the list
Refer to the docs.

how to find nearest value of each elements in array?

I want to find range between closest value of this elements.
Delta value between elements. And it would be positive number because its modulus.
class Element {
double DeltaValue;
double ElementValue;
public Element(double n) {
ElementValue = n;
}
static void Main() {
list<Element> ListElements = new list<Elements>;
ListElements.Add(3);
ListElements.Add(10);
ListElements.Add(43);
ListElements.Add(100);
ListElements.Add(30);
ListElements.Add(140);
for(int i = 0; i < ListElements.Count; i++) {
ListElements[i].DeltaValue = //and problem is here
//example as for ListElements[2].DeltaValue will be 13; because 43-30=13;
}
//example as for ListElements[2].DeltaValue will be 13; because 43-30=13;
Just sort the array in increasing order and the smallest difference between the previous and the next element of the current element will solve your problem. Here for last element you can just look at the difference of its previous element.
Should be able to do it in one line with linq via the following:
public static int GetClosestVal(this int[] values, int place)
{
return values.OrderBy(v => Math.Abs(v - values[place])).ToArray()[1];
}
The following outputs 30
var testArray = new [] {3, 10, 43, 100, 30, 140};
Console.Write(testArray.GetClosestVal(2));
Basically speaking you sort by the absolute difference between each item and the chosen item, then grab the second item in the list since the first will always be the item itself (since n-n=0)
Thus the sorted list should be [43, 30, 20, 3, 100, 140]
I'm not sure, whether I understand your question right. If I have, then the following code snippet can help you:
class Program
{
static void Main(string[] args)
{
Elements ListElements = new Elements();
ListElements.ElementValue.Add(3);
ListElements.ElementValue.Add(10);
ListElements.ElementValue.Add(43);
ListElements.ElementValue.Add(100);
ListElements.ElementValue.Add(30);
ListElements.ElementValue.Add(140);
ListElements.CreateDeltaValues();
for (int i = 0; i < ListElements.DeltaValue.Count; i++)
{
Console.WriteLine("ListElement["+i+"]: " + ListElements.DeltaValue[i]);
//example as for ListElements[2].DeltaValue will be 13; because 43-30=13;
}
Console.ReadKey();
}
}
public class Elements
{
public List<double> DeltaValue = new List<double>();
public List<double> ElementValue = new List<double>();
public void CreateDeltaValues()
{
this.ElementValue.Sort();
for (int i = 1; i < this.ElementValue.Count; i++)
{
var deltaValue = this.ElementValue[i] - this.ElementValue[i-1];
this.DeltaValue.Add(deltaValue);
}
}
}
It's a console application, but this code should work also for other app models.
This code generates the following output:

List to align, easiest way to realize

I have following list:
100 -> 1.0 99 -> 1.1 98 -> 1.1 97 -> 1.2 ...
23-28 -> 5.6 ... 0-5 -> 6.0
On the left side are the maximal reached points, on the right side the grade.
This list contains around 40 different Points -> Grade. So my program is calculating the points of the exam, and in the end it should say 100 Points reached, u got the grade 1.0 ... 3 Points reached -> 6.0 ...
On my current knowledge, I know only switch case, but I think it's not the best way to realize it.
I'd start off with a data structure for the list you have. (This is assuming C# 6, by the way - for earlier versions of C# you wouldn't be able to use an auto-implemented readonly property, but that's about the only difference.)
public sealed class GradeBand
{
public int MinScore { get; }
public int MaxScore { get; } // Note: inclusive!
public decimal Grade { get; }
public GradeBand(int minScore, int maxScore, decimal grade)
{
// TODO: Validation
MinScore = minScore;
MaxScore = maxScore;
Grade = grade;
}
}
You can construct your list with:
var gradeBands = new List<GradeBand>
{
new GradeBand(0, 5, 6.0m),
...
new GradeBand(23, 28, 5.6m),
...
new GradeBand(100, 100, 1.0m),
};
You'd probably want some sort of validation that the bands covered the whole range of grades.
Then there are two fairly obvious options for finding the grade. Firstly, a linear scan with no preprocessing:
public decimal FindGrade(IEnumerable<GradeBand> bands, int score)
{
foreach (var band in bands)
{
if (band.MinScore <= score && score <= band.MaxScore)
{
return band.Grade;
}
}
throw new ArgumentException("Score wasn't in any band");
}
Or you could preprocess the bands once:
var scoreToGrade = new decimal[101]; // Or whatever the max score is
foreach (var band in bands)
{
for (int i = band.MinScore; i <= band.MaxScore; i++)
{
scoreToGrade[i] = band.Grade;
}
}
Then for each score you can just use:
decimal grade = scoreToGrade[score];
Try this sample
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
Dictionary<int, double> dictionary =
new Dictionary<int, double>();
dictionary.Add(100, 1.0);
dictionary.Add(99, 1.1);
//Add more item here
// See whether Dictionary contains a value.
if (dictionary.ContainsKey(100))
{
double value = dictionary[100];
Console.WriteLine(String.Format("{0:0.0}",value));
}
Console.ReadLine();
}
}

Use LINQ To find multiple instances of Min() in Object and change the element(s)

I've the following classes;
public class PricePlan
{
public string Name { get; set; }
public List<Price> Prices { get; set; }
public PricePlan()
{
Prices = new List<Price>();
}
}
public class Price
{
public DateTime Date { get; set; }
public decimal Rate { get; set; }
public bool Free { get; set; }
public Price()
{
Free = false;
}
}
And then the following to populate the object and list;
PricePlan oPricePlan = new PricePlan();
oPricePlan.Name = "Standard Rate Plan";
Price oPrice;
DateTime oDate = DateTime.Today;
for (int x = 1; x < 10; x++)
{
oPrice = new Price();
oPrice.Date = oDate.AddDays(x);
oPrice.Rate = 10 * x;
oPricePlan.Prices.Add(oPrice);
}
oPrice = new Price();
oPrice.Date = oDate.AddDays(11);
oPrice.Rate = 10;
oPricePlan.Prices.Add(oPrice);
The sample data might be:
02/01/2013,10,False
03/01/2013,20,False
04/01/2013,30,False
05/01/2013,40,False
06/01/2013,50,False
07/01/2013,60,False
08/01/2013,70,False
09/01/2013,80,False
10/01/2013,90,False
12/01/2013,10,False
Using
oPricePlan.Prices.Min(r => r.Rate)
I get get the Min value for the Rate or IndexOf[] can return the first instance. However, I'm wanting to return X number of lowest rates. For example how can I set the following;
For 1 Min rate (two rates might have the same Min) in the system, set it to 0 zero and the Free bool to true
For 2 Min rates (that might be the same), set it to 0 zero and the Free bool to true
So basically I'm wanting to find the lowest X number of rates, change the actual lowest rates found, and set the Free bool flag to true.
Should I look at using LINQ, or is their a preferred way ?
int numberOfItems = 1;
var orderedPrices = oPricePlan.Prices.OrderBy(x => x.Rate).ToList();
decimal targetRate = orderedPrices[numberOfItems - 1].Rate;
foreach (var price in orderedPrices.TakeWhile(x => x.Rate <= targetRate))
{
price.Rate = 0;
price.Free = true;
}
Edit: The above is based on selecting a targetRate based on numberOfItems, and then setting all items less than or equal to that to 0 (which might be numberOfItems or a little more items). Originally I had:
For your example input, this code will select one of the items with a rate of 10 (it'll be whichever happened to come first in oPricePlan.Prices since OrderBy is stable). That is, it is the number of items, not the number of distinct rates. I think that's what you're asking for; otherwise a solution like Tim Schmelter's is right.
int numberOfItems = 1;
foreach (var price in oPricePlan.Prices.OrderBy(x => x.Rate).Take(numberOfItems))
{
price.Rate = 0;
price.Free = true;
}
You could use OrderBy + GroupBy, Take and a loop:
var priceGroups = oPricePlan.Prices
.OrderBy(p => p.Rate) // order by rate ascending
.GroupBy(p => p.Rate) // group by rate
.First() // use the lowest price-rate group only
.Take(2); // change 2 to 1 if you only want to modify one price in this min-group
foreach (Price price in priceGroups)
{
price.Rate = 0;
price.Free = true;
}

Categories

Resources