Knapsack solution for input of strings - java

I had an online coding test where the problem was described as below. I could not finish the question but did leave some comments on how to come up with a potential solution. I wanted to know what have been the best to solve this problem. The main issue I was having was splitting the given inputs as string and finding a suitable collection to put them in. Below is the question.
You want to send your friend a package with different things.
Each thing you put inside of a package has such parameters as index number, weight and cost.
The package has a weight limitation.
Your goal is to determine which things to put into the package so that the total weight is less than or equal to the package limit and the total cost is as large as possible.
You would prefer to send a package which has less weight if there is more than one package with the same price.
This is a variation of the Knapsack problem.
Input:
Your program should read lines from standard input. Each line contains the weight that a package can take (before the colon) and the list of things you need to pick from. Each thing is enclosed in parentheses where the 1st number is a thing's index number, the 2nd is its weight and the 3rd is its cost.
Max weight any package can take is <= 100.
There might be up to 15 things you need to choose from.
Max weight and max cost of any thing is <= 100.
Output:
For each set of things produce a list of things (their index numbers separated by comma) that you put into the package. If none of the items will fit in the package, print a hyphen (-).
Test 1
Input81 : (1,53.38,$45) (2,88.62,$98) (3,78.48,$3) (4,72.30,$76) (5,30.18,$9) (6,46.34,$48)
Expected Output 4
Test 2
Test Input 75 : (1,85.31,$29) (2,14.55,$74) (3,3.98,$16) (4,26.24,$55) (5,63.69,$52) (6,76.25,$75) (7,60.02,$74) (8,93.18,$35) (9,89.95,$78)
Expected Output 2,7

There might be up to 15 things you need to choose from. so you have possible combination 2^15 = 32,768. So you can
check every combination and find out which combination meet requirement.
Exampe: there is 3 (1,2,3) things.
then u can choose: (),(1),(2),(3),(1,2),(1,3),(2,3),(1,2,3).
Now need tho find which combination meet requirements.
Here is solution:
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String s = in.nextLine();
String res[] = s.replaceAll("[^0-9.]+",";").split(";");
double target = Integer.parseInt(res[0]);
int n = (res.length-1) / 3;
int[] Index = new int[20];
double[] P = new double[20];
double[] W = new double[20];
int jj = 0;
for(int i = 1; i < res.length; i +=3){
Index[jj] = Integer.parseInt(res[i]);
W[jj] = Double.parseDouble(res[i+1]);
P[jj++] = Double.parseDouble(res[i+2]);
}
double result = 0;
int track = 0;
double resSum = 0;
for(int i =0; i< (1<<n); i++){
double sum = 0;
double weight = 0;
for(int j=0; j < n; j++){
if(((1<<j)&i) > 0){
sum+= P[j];
weight+=W[j];
}
}
if(weight <= target){
if(sum > resSum){
result = weight;
track = i;
resSum = sum;
}else if(sum == resSum && weight < result){
result = weight;
track = i;
}
}
}
jj = 0;
for(int i = 0; i < n; i++){
if(((1<<i)&track) > 0){
if(jj > 0){
System.out.print(",");
}
jj = 1;
System.out.print(Index[i]);
}
}
if(track == 0){
System.out.println("-");
}else {
System.out.println();
}
}

Related

How to rotate 2-D Array in Java

[SOLVED]
The title of this question is vague but hopefully this will clear things up.
Basically, what I am looking for is a solution to rotating this set of data. This data is set up in a specific way.
Here is an example of how the input and output would look like:
Input:
3
987
654
321
Output:
123
456
789
The '3' represents the number of columns and rows that will be used. If you input the number '4', you will be allowed to input 4 sets of 4 integers.
Input:
4
4567
3456
2345
1234
Output:
1234
2345
3456
4567
The goal is to find a way to rotate the data only if needed. You have to make sure the smallest corner number is at the top left. For example, for the code above, you rotated it so 1 is at the top left.
The problem I have is that I don't know how to rotate the data. I am only able to rotate the corners but not the sides. This is what my code does so far:
take the input of each line and turn them into strings
split those strings into separate characters
store those characters in an array
I just do not know how to compare those characters and in the end rotate the data.
Any help would be appreciated! Any questions will be answered.
A detailed description of the problem is here(problem J4).
This is just a challenge I assigned myself for practice for next year's contest, so giving me the answer won't "spoil" the question, but actually help me learn.
Here is my code so far:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner kb = new Scanner(System.in);
int max = kb.nextInt();
int maxSqrt = (max * max);
int num[] = new int[max];
String num_string[] = new String[max];
char num_char[] = new char[maxSqrt];
int counter = 0;
int counter_char = 0;
for (counter = 0; counter < max; counter++) {
num[counter] = kb.nextInt();
}
for (counter = 0; counter < max; counter++) {
num_string[counter] = Integer.toString(num[counter]);
}
int varPos = 0, rowPos = 0, charPos = 0, i = 0;
for (counter = 0; counter < maxSqrt; counter++) {
num_char[varPos] = num_string[rowPos].charAt(charPos);
i++;
if (i == max) {
rowPos++;
i = 0;
}
varPos++;
if (charPos == (max - 1)) {
charPos = 0;
} else {
charPos++;
}
}
//
for(int a = 0 ; a < max ; a++){
for(int b = 0 ; b < max ; b++)
{
num_char[counter_char] = num_string[a].charAt(b);
counter_char++;
}
}
//here is where the code should rotate the data
}
}
This is a standard 90 degree clockwise rotation for a 2D array.
I have provided the solution below, but first a few comments.
You said that you're doing this:
take the input of each line and turn them into strings
split those strings into separate characters
store those characters in an array
Firstly youre essentially turning a int matrix into a character matrix. I do not think you need to do this, since even if you do want to compare values, you can use the ints provided.
Secondly, there is no need to compare any 2 data elements in the matrix, since the rotation does not depend on any value.
Here is an adapted solution for java, originally written in C# by Nick Berardi on this question
private int[][] rotateClockWise(int[][] matrix) {
int size = matrix.length;
int[][] ret = new int[size][size];
for (int i = 0; i < size; ++i)
for (int j = 0; j < size; ++j)
ret[i][j] = matrix[size - j - 1][i]; //***
return ret;
}
If you wanted to do a counterCW rotation, replace the starred line with
ret[i][j] = matrix[j][size - i - 1]

Moving Average Using User-Input Array

I need to write a program that calculates a moving average by a user inputted array. The first element of the array is the window size, and the input is terminated by a 0. The output values are printed with two digits after the decimal point.
Example input: 3 2 4 7 7 8 11 12 0
Corresponding Output: 4.33 6.00 7.33 8.67 10.33
(4.33 is average of 2,4,7 and 6 is average of 4,7,7 etc.)
Here's my code so far:
package movingaverage;
import java.util.Scanner;
public class MovingAverage {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] arr = new int[n];
int sum = 0;
for (int i = 0; i < n; i++) {
sum += arr[i];
}
avg[0] = sum / 5;
int j = 1;
for (int i = 5; i < arr.length; i++) {
sum = sum + arr[i] - arr[i - 5];
avg[j++] = sum / 5;
}
}
}
I think I have the loop right, but I'm not sure how to get the array to end at 0.
This is a possible solution.
public class Test
{
private static final Scanner SCANNER;
static {
SCANNER = new Scanner(System.in);
}
public static final void main(final String... args) {
final String[] numbers = SCANNER.nextLine().trim().split(" ");
final int consideredElements = Integer.parseInt(numbers[0]);
float sum = 0;
int value = 0;
for (int i = 1; i < numbers.length; i++) {
sum = 0;
for (int k = 0; k < consideredElements; k++) {
value = Integer.parseInt(numbers[i + k]);
if (value == 0) {
return;
}
sum += value;
}
System.out.println(new BigDecimal(sum / consideredElements).setScale(2, RoundingMode.HALF_EVEN));
}
}
}
First, you are using 5 in a couple of places in your program, I see no justification for that. Could it be that your expectation of user input lead you to put 5 where the number you really should use, depends on user input? Maybe you should use the window size instead? I’m guessing a bit here.
Next, as #lppEdd pointed out, you are not reading the numbers from your input — only the window size.
Next, you are declaring your array of size n, which I believe was your window size, not your array size. I believe the real solution to this problem is using better and more explanatory variable names.
Your code does not compile since you have not declared the array avg that you try to store your moving average into.
Fifth, when you want your average as a double, you need to convert to double before dividing (this is a classic pitfall that has already generated many questions on Stack Overflow).
I hope this gets you a couple of steps further.

Java Random Utility Generating Too Many 0's And Static Numbers

The line birthdays[j] = rnd.nextInt(365); seems to generate extra 0's in the int[] birthdays array. It also seems to add an EXTRA 0 into the array and generate static values depending on how many simulations I run and how many birthdays I generate. For instance, if I do 5 simulations and enter a 3 for the number of people in each simulation's "birthday pool" I always get an array of [0, 0, 289, 362].
Any help understanding the problem would be greatly appreciated.
public static void main(String[] args) {
System.out.println("Welcome to the birthday problem Simulator\n");
String userAnswer="";
Scanner stdIn = new Scanner(System.in);
do {
int [] userInput = promptAndRead(stdIn);
double probability = compute(userInput[0], userInput[1]);
// Print results
System.out.println("For a group of " + userInput[1] + " people, the probability");
System.out.print("that two people have the same birthday is\n");
System.out.println(probability);
System.out.print("\nDo you want to run another set of simulations(y/n)? :");
//eat or skip empty line
stdIn.nextLine();
userAnswer = stdIn.nextLine();
} while (userAnswer.equals("y"));
System.out.println("Goodbye!");
stdIn.close();
}
// Prompt user to provide the number of simulations and number of people and return them as an array
public static int[] promptAndRead(Scanner stdIn) {
int numberOfSimulations = 0;
while(numberOfSimulations < 1 || numberOfSimulations > 50000) {
System.out.println("Please Enter the number of simulations to do. (1 - 50000) ");
numberOfSimulations = stdIn.nextInt();
}
int sizeOfGroup = 0;
while(sizeOfGroup < 2 || sizeOfGroup > 365) {
System.out.println("Please Enter the size of the group of people. (2 - 365) ");
sizeOfGroup = stdIn.nextInt();
}
int[] simulationVariables = {numberOfSimulations, sizeOfGroup};
return simulationVariables;
}
// This is the method that actually does the calculations.
public static double compute(int numOfSims, int numOfPeeps) {
double numberOfSims = 0.0;
double simsWithCollisions = 0.0;
int matchingBirthdays = 0;
int[] birthdays = new int[numOfPeeps + 1];
int randomSeed = 0;
for(int i = 0; i < numOfSims; i++)
{
randomSeed++;
Random rnd = new Random(randomSeed);
birthdays = new int[numOfPeeps + 1];
matchingBirthdays = 0;
for(int j = 0; j < numOfPeeps; j++) {
birthdays[j] = rnd.nextInt(365);
Arrays.sort(birthdays);
}
for(int k = 0; k < numOfPeeps; k++) {
if(birthdays[k] == birthdays[k+1]) {
matchingBirthdays++;
}
}
if(matchingBirthdays > 0) {
simsWithCollisions = simsWithCollisions + 1;
}
}
numberOfSims = numOfSims;
double chance = (simsWithCollisions / numberOfSims);
return chance;
}
}
The line "birthdays[j] = rnd.nextInt(365);" seems to generate extra 0's in the int[] birthdays array.
Well, it doesn't. The array elements where zero to start with.
What that statement actually does is to generate a single random number (from 0 to 364) and assign it to one element of the array; i.e. the jth element. That is not what is required for your problem.
Now, we could fix your code for you, but that defeats the purpose of your homework. Instead I will give you a HINT:
The birthdays array is supposed to contain a COUNT of the number of people with a birthday on each day of the year. You have to COUNT them. One at a time.
Think about it ...
int arrays are by default initialized to 0 unless explicitly specified. Please see this Oracle tutorial about Arrays.
I found the problem myself. The issue was that having the "Arrays.sort(birthdays);" statement inside of a loop. That generated extra 0's.

ArrayIndexOutOfBoundsException when trying to reverse an array

This seems simple enough but I get the error "Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
at reverse.main(reverse.java:28)"
I initially take inputs from the user to write an array, and then I want to print the array backwards. I understand there are other ways of doing this, but I mainly want to know why this is not working. Going through it line by line makes sense?
PS. If it's not a problem, is there any better way of doing this?
import java.util.Scanner;
public class reverse {
/**
* #param args
*/
public static void main(String[] args) {
System.out.printf("Enter the number of values in array: ");
Scanner scanner = new Scanner(System.in);
int n;
n = scanner.nextInt();
double[] a1 = new double[n];
int i;
System.out.printf("Enter the value in the array: ");
for (i = 0; i < n; i++){
Scanner scanner2 = new Scanner(System.in);
a1[i] = scanner2.nextInt();
}
double j;
double k;
for (i = 0; i < n/2; i++){
j = a1[i];
k = a1[n-i]; //error line;
a1[i]=k;
a1[n-i]=j;
}
for(i = 0; i < n; i++){
System.out.println(" "+a1[i]);
}}
}
When i = 0, n-i will result in n, which is one larger than the available indexes( 0 -> n-1 ).
for (i = 0; i < n/2; i++){
j = a1[i];
k = a1[n-i]; //error line;
a1[i]=k;
a1[n-i]=j;
}
Collections.reverse(Arrays.asList(array))
Will reverse an array for you, then just print its values out. It's great to do these kinds of problems as exercises but if you're ever woring in the industry it's usually better to rely on the Java API for trivial things like this. Probably going to be faster and a lot more simpler than anything you can come up with.
As said by Samhain, when i = 0, then n-i == n, which is greater than the last index of the array (since arrays start with index 0).
The simplest solution is to just subtract an additional 1 from n-i.
j = a1[i];
k = a1[n-i-1];
a1[i]=k;
a1[n-i-1]=j;
Also, creating a new Scanner is totally unnecessary. Just continue to use the first one you created.
for (i = 0; i < n; i++){
a1[i] = scanner.nextInt();
}
Finally, for what it's worth, if you're using nextInt you don't need to declare your array as a double[] (nor do j and k need to be doubles). You can just use ints.
Here's it running on ideone.

Why aren't my variables being altered in my loops?

I've got a method that is meant to work out the group of elements of a 3D array with the highest combined value. I've got 3 nested loops that I'm using to go through my array, and as certain conditions are met I want to alter the variables. However none of the variables are being used. I ant to alter int y and int m to whatever iteration of the for loop it's on if sum exceeds total.
Thanks. Here is my code:
public void wettestMonth(){
double sum = 0;
double total = 0;
int y = 0;
int m = 0;
//cycle through each year and month
for(int i = 0; i < 34; i++){
for(int j = 0; j < 12; j++){
//reset the current month to 0 after each month has been cycled through
sum = 0;
for(int k = 0; k < 31; k++){
//only add the record if the current entry is not null (-99.99)
if(sortedData[i][j][k] != -99.99){
sum += sortedData[i][j][k];
}
//if the current month is wetter than the wettest one, make the current month the new wettest one
if(sum > total){
total = sum;
y = i;
m = j;
}
}
}
}
JOptionPane.showMessageDialog(null, "The wettest month on record was " +m +y, "Wettest Month.", JOptionPane.PLAIN_MESSAGE);
}
Edit, I just reconstructed it with while loops instead and I'm getting an out-of-bounds error on what appears to be the problem line, if(sortedData[i][j][k] != -99.99)
Edit 2, here is where I declare and initialise sortedData[][][]
public class GetData {
//initialises an array that holds 34 years, each with 12 months, each of which has 31 entries for reach day
public double[][][] sortedData = new double[34][12][31];
//initialises a new scanner named rainFile
private Scanner rainFile;
//method for opening the file
public void openFile() {
try{
//as the input for the scanner we use the rainfall file
rainFile = new Scanner(new File("C:\\\\Users\\\\admin\\\\Documents\\\\NetBeansProjects\\\\110_term3\\\\WeatherDataFiles\\\\rainfall.txt"));
}
catch(Exception e){
//if no file has been found a JOptionPane will display an error message telling the user to double-check the file path
JOptionPane.showMessageDialog(null, "Check the file path is correct.", "No file found!", JOptionPane.ERROR_MESSAGE);
}
}
//method for reading the file
public void readFile(){
//ignore the first 3 lines in the data file
String dump1 = rainFile.nextLine();
String dump2 = rainFile.nextLine();
String dump3 = rainFile.nextLine();
//these nested for loops will dictate the current index of sortedData
for(int i = 0; i < 34; i++){
for(int j = 0; j < 12; j++){
//ignores the year and month at the start of each line
String dump4 = rainFile.next();
String dump5 = rainFile.next();
//this final nested for loop dictates the final index of sortedData
for(int k = 0; k < 31; k++){
//asigns the current value of scanner rainFile to String a
String a = rainFile.next();
//converts the String a to a double type and then assigns it to the current index of sortedData
double dbl = Double.parseDouble(a);
sortedData[i][j][k] = dbl;
}
}
}
}
Have you tried printing out the sum for each month?
The most obvious possibility is that your sums are always less than 0 because of a buggy equality check.
For this line,
sortedData[i][j][k] != -99.99
It will be true unless the value is exactly what -99.99 rounds to. This may be unintended. For example, if you are constructing the value through floating point math somehow, you most likely won't get exactly the same value due to rounding errors. Furthermore, use of a weird sentinel value like this is error prone and less readable. You're better off using an obvious sentinel value like NaN if you can.
To see the problem, consider what would happen if your values are slightly different. Say, -99.99000001. Then after the first day, you already have a negative value. After a month, the sum will be approximately -3099.69000031, much less than 0. Since the sum is always negative, it is never better than the original total 0, so the best never gets updated.
You probably also want to move the update check outside of the day loop. This part looks like it's supposed to use the sum for the whole month, but you are running it with the partial sum of every day of the month. It won't actually cause incorrect results as long as the values being added are nonnegative (but they probably aren't due to the aforementioned bug), but you should still fix it.
if(sum > total){
total = sum;
y = i;
m = j;
}
I don't see anything wrong on your code.
Maybe the following condition never evals to true?
if(sortedData[i][j][k] != -99.99)
Carefully, reviewed the first code snippet that has been provided, it appears to me that you have been doing if check for the size inside the for loop, moved the for loop vars to the finals, added some further comments.
You could try and run your swing app through the Eclipse debug and see what results you get at each line in your application ?
Would expect it to work given correct input 3D array))
/**
* This method calculates the wettest month during the certain period of time.
*/
public void wettestMonth(){
double sum = 0;
double total = 0;
int y = 0;
int m = 0;
final int numberOfYearsToCycleThrough = 34;
final int numberOfMonthsToCycleThrough = 12;
//cycle through each year and month
for (int i = 0; i < numberOfYearsToCycleThrough; i++) {
for (int j = 0; j < numberOfMonthsToCycleThrough; j++) {
sum = 0;
for (int k = 0; k < 31; k++){
//only add the record if the current entry is not null (-99.99)
if (sortedData[i][j][k] != null && sortedData[i][j][k] != -99.99) {
sum += sortedData[i][j][k];
}
}
//if the current month is wetter than the wettest one, make the current month the new wettest one
if (sum > total) {
total = sum;
y = i;
m = j;
}
}
}
JOptionPane.showMessageDialog(null, "The wettest month on record was " +m +y, "Wettest Month.", JOptionPane.PLAIN_MESSAGE);
}

Categories

Resources