USACO(JAVA) : Algorithms Complete Search - java

So here is the link to problem statement : http://train.usaco.org/usacoprob2?a=ZSMwtXwq7ro&S=comboProblem Statement.
EDIT 1: So the problem is this :
There is a lock and there are 2 valid 3 digit combinations for the lock. One which is set by the user and other one is the master key set by the manufacturer. Also the lock has certain tolerance for errors, ie it will open even if the numbers on the dials are each within at most 2 positions of a valid combination.
For example , suppose user set key was 1, 2, 3 and the master key (manufacturer set) was 4, 5, 6. For these 2 keys 1, 3, 5 is a valid key since the difference between each digit(at same position) of this key and user set key is atmost 2 . But 1, 5, 6 is an invalid combo because the difference between digits of this key and user set key > 2 and same for master key.
Basically what I am doing is pretty naive, I am generating all possible lock combinations and checking for validity of each combination. Here is my code
import java.util.*;
public class combo {
public static void main(String[] args){
Scanner myScanner = new Scanner(System.in);
int N = myScanner.nextInt();
int[] keys = new int[3];
int[] masterKeys = new int[3];
for(int i = 0; i < 3; i++){
keys[i] = myScanner.nextInt();
}
for(int i = 0; i < 3; i++){
masterKeys[i] = myScanner.nextInt();
}
int cnt = 0;
int[] combo = new int[3];
for(int i = 1; i <= N; i++){
combo[0] = i;
for(int j = 1; j <= N; j++){
combo[1] = j;
for(int k = 1; k <=N; k++){
combo[2] = k;
if(validCombo(combo, keys, masterKeys)){
cnt += 1;
}
}
}
}
System.out.println(cnt);
}
// bug here
/*
Valid
combo : 1, 3, 5
key : 1, 2, 3
master 4, 5, 6
Invalid
1 5 6
*/
public static boolean validCombo(int[] combo, int[] keys, int[] masterKeys){
boolean checkKeys = true;
boolean checkMasterKeys = true;
for(int i = 0; i < 3; i++){
if(Math.abs((int)(combo[i]-keys[i])) > 2){
checkKeys = false;
}
if(Math.abs((int)(combo[i]-masterKeys[i])) > 2){
checkMasterKeys = false;
}
}
return checkKeys | checkMasterKeys;
}
}
So for inputs N = 50 , keys = 1, 2, 3 and masterKeys = 5, 6,7 , I get output 184 but the correct output is 249 (sample given test case). Can anyone please just give me a hint as to what is wrong with my logic

You aren't taking into account the fact that the numbers wrap around - i.e., when N = 50, then 50 is 1 away from 1, 2 away from 2, etc.
When trying to debug something like this, it might help if you printed out exactly what your program was counting as solutions, and then comparing to the output listed on the problem site, if they give you such details, or just using the extra information to validate your own thought process.

Instead of "trying" all combinations you could compute them
compute the number of overlapping numbers per dial
if distance between the key number and the master key number
is >= 5 --> you have 10 distinct values
is < 5 --> you have (5 - distance) overlapping numbers
examples:
key: 3 master key: 8 distinct numbers: 10 = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
key: 3 master key: 6 distinct numbers: 8 = 1, 2, 3, 4, 5, 6, 7, 8
4 and 5 are the overlapping numbers for this dial
if at least for one dial there is no overlapping number then we have the maximum of 250 combinations
if all dials have at least one overlapping number we can compute the
number of overlapping combinations by multiplying the overlapping numbers
of all dials
unique combinations can be computed as max. number - overlapping combinations
example: key: 3, 4, 5 master key: 7, 8, 9
1 2 3 -+
2 3 4 |
3 4 5 |<-- the combinations close to the key
4 5 6 |
5 6 7 -+<-- the only overlapping number
6 7 8 |
7 8 9 |<-- the combinations close to the master key
9 10 11 |
10 11 12 -+
There are 249 valid combinations.
Here a short snippet for the computation.
int numbersPerDail = 50;
int dials = 3;
int[] keys = {2, 2, 3};
int[] masterKeys = {48, 5, 6};
int[] overlappingNumbers = new int[dials];
for (int i = 0; i < dials; i++) {
int distance = Math.max(keys[i], masterKeys[i]) - Math.min(keys[i], masterKeys[i]);
if (distance >= 46) { // the dial is circular
distance = numbersPerDail - distance;
}
overlappingNumbers[i] = 5 - distance;
}
int doubleCombos = 0;
if (overlappingNumbers[0] > 0 && overlappingNumbers[1] > 0 && overlappingNumbers[2] > 0) {
doubleCombos = overlappingNumbers[0] * overlappingNumbers[1] * overlappingNumbers[2];
}
System.out.println("valid combinations = " + (250 - doubleCombos));

Related

Path with maximum average value

import java.util.Scanner;
public class maxAvgPath {
public static void main(String [] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[][] c = new int[n+1][n+1];
for (int i = 0; i < n; i++){
for (int j = 0; j < n; j++){
c[i][j]=sc.nextInt();
}
}
float sum = c[0][0];
float m = 0;
for (int i = 0; i < n; i++){
for (int j = 0; j < n; j++){
if (c[i][j + 1] < c[i + 1][j]) {
sum = sum + c[i + 1][j];
m++;
} else {
sum = sum + c[i][j + 1];
m++;
}
}
}
System.out.println((sum/m));
}
}
Given a square matrix of size N*N, where each cell is associated with a specific cost. A path is defined as a specific sequence of cells which starts from the top-left cell move only right or down and ends on bottom right cell. We want to find a path with the maximum average over all existing paths. Average is computed as total cost divided by the number of cells visited in the path.
Input : Matrix = [1, 2, 3,
4, 5, 6,
7, 8, 9]
Output : 5.8
Path with maximum average is, 1 -> 4 -> 7 -> 8 -> 9
Sum of the path is 29 and average is 29/5 = 5.8
But I am not getting the correct output can some tell where is my mistake?
Your m should always be 2 * n - 1. Regardless of the chosen path, you will always move either right or down, starting from top-left corner. You have n - 1 rows and n - 1 columns, so you're moving right exactly n - 1 and moving down exactly n - 1 times. In total, counting the top-left, you have 1 + (n - 1) + (n - 1) numbers on your ideal path. Therefore, you're not looking for a max average, you're actually looking for a maximum sum.
Your loops are not exactly in sync with what you're trying to do. Let's say, for the given matrix, when i = 0, j is 0, that you discover that 4 is bigger than 2. You pick the 4, m[1][0], and then you should check the max of m[1][1] and m[2][0] ... but your loop moves to ... i = 0, j = 1, allowing you to check m[1][2] and m[1][1].
You're also using a Greedy aproach which won't always give you the best result, e.g. [1 2 9, 3 4 9, 5 2 9], your algorithm will pick 1, move down to 3 and 5 then right to 2 and 9, giving you a total of 20. However, the ideal path is 1, right to 2 and 9, then down to 9 and 9, for a grand total of 30.
One approach to solve this is to keep the loops, but instead of maintaining one sum, you need to keep track of multiple sums and picking a max at every step.
e.g. for [1 2 9, 3 4 9, 5 2 9], you initialize your sum matrix with 0 and start navigating.
For sum[0][0], there is only one possible sum, which is picking up m[0][0]. This continues for the entire 1st row, so your sum matrix is now [1 3 12, 0 0 0, 0 0 0]. For sum[1][0], there is also only one possible sum, moving down from m[0][0], so sum[1][0] is m[0][0] + m[1][0]. And here the fun starts.
For sum[1][1], you need to pick between sum[0][1] and sum[1][0] and add m[1][1]. You will of course pick sum[1][0] (which is really m[0][0] + m[1][0]) and add m[1][1] to it, so sum[1][1] is 8. Therefore, if your maximal path ever travels to r1c1, you know that you've picked the maximum possible sum so far.
I will also highlight the next step, sum[1][2], where you have to pick between sum[0][2] and sum[1][1] and add m[1][2]. We know sum[0][2] is 12, sum[1][1] is 8, so, if our path ever reaches cell r1c2, we know that we need to pick first row entirely and then move down instead(m[0][0], m[0][1], m[0][2], m[1][2]) of moving down and picking the next row (m[0][0], m[1][0], m[1][1], m[1][2]), so your current maximal sum in sum[1][2] is 21 and your sum matrix is [1 3 12, 4 8 21, 0 0 0].
Final row is the same thing:
sum[2][0] = sum[1][0] + m[2][0]
sum[2][1] = max(sum[1][1], sum[2][0]) + m[2][1]
sum[2][2] = max(sum[1][2], sum[2][1]) + m[2][2]
Your sum matrix eventually becomes [ 1 3 12, 4 8 21, 9 11 30 ], therefore your
max is 30. You have picked up 2 * 3 - 1 = 5 numbers, so your average is 6.
Illustrating the sum matrix is also interesting:
1 3 12
4 8 21
9 11 30
If you ever want to determine what was the path you've chosen from top-left to bottom-right, you just need to follow the sums, in descending order(keeping in mind the move-right or move-down rule), 30 -> 21 -> 12 -> 3 -> 1.
I will leave the coding part as an exercise, you have all the details you need.
(sorry for bad formatting and long text)

Maximum difference between final and initial sum

I am new to programming and having trouble solving one task. I have several input example. First line contains two numbers m (number of digits on paper, 1<m<1000) and h (limit on the number of operations, 1<h<1000). I have the opportunity, no more than h times, to take any number from a piece of paper (means m), then paint over one of the old digits, and write a new arbitrary digit in its place. By what maximum value can I be able to increase the sum of all the numbers on the piece of paper?
First example:
Input:
5 2 //m and h
1 3 1 4 5 //m = 5, so I can add 5 arbitrary numbers and h=2, so I can change 2 numbers
Output:
16 // cause I changed 1 and 1 to 9 and 9, so the difference 8 and 8 and the sum is 16
Second example:
Input:
3 1
99 5 85
Output:
10 //85 to 95, so the difference is 10
Third example:
Input:
1 10
9999
Output:
0 // nothing to be change
What I have for now:
Scanner sc = new Scanner(System.in);
System.out.println("Enter the number: ");
int m = sc.nextInt();
int h = sc.nextInt();
System.out.println("Entered: " + m);
System.out.println("Entered: " + h);
int[] numbers = new int[m];
for(int i = 0; i < m; ++i) {
numbers[i] = sc.nextInt();
}
Arrays.sort(numbers);
//here is my logic: I am changing 1 to 9
for (int i = 0; i < h; i++) {
if (numbers[i] < 10) {
numbers[i] = 9;
}
else if (numbers[i] > 9 and numbers[i] < 100) {
numbers[i] = 99;
}
}
sc.close();
My logic can work for the first example, but for the second example it won't work. Can you assist me if I am using right logic or is there any easier way to solve this? Thanks in advance.
I quickly came up with below solution
public static void calculateMax(int m,int h, int[] arr){
int sum = 0;
ArrayList<Integer> al = new ArrayList<>();
for(int i=0;i<arr.length;i++){
String stringNum = Integer.toString(arr[i]);
int num = Integer.parseInt(stringNum.substring(0, 1));
if(num!=9){
al.add(Integer.parseInt(("9"+stringNum.substring(1)))-arr[i]);
continue;
}
al.add(0);
}
Collections.sort(al);
int j = al.size()-1;
for(int i=0;i<h && j>0;i++){
sum+=al.get(j--);
}
System.out.println(sum);
}
Here what I am doing is basically, calculating for each number what we can get as maximum by removing one digit and replacing by 9. And I am storing them in a list. Then we can sort that list and get 'h' largest numbers from stored list, and essentially getting the sum of them and printing it.
Break each input number into its digits times the appropriate power of 10. Sort these by powers of 10 descending, digits ascending. Apply your operation in this order.
E.g., 876, 12, 42 -> 800, 70, 6, 10, 2, 40, 2 -> 800, 10, 40, 70, 2, 2, 6.

Find the maximum number of flags that can be set on mountain peaks

I worked with a Codility problem provided below,
A non-empty array A consisting of N integers is given.
A peak is an array element which is larger than its neighbours. More precisely, it is an index P such that 0 < P < N − 1 and A[P − 1] < A[P] > A[P + 1].
For example, the following array A:
A[0] = 1
A[1] = 5
A[2] = 3
A[3] = 4
A[4] = 3
A[5] = 4
A[6] = 1
A[7] = 2
A[8] = 3
A[9] = 4
A[10] = 6
A[11] = 2
has exactly four peaks: elements 1, 3, 5 and 10.
You are going on a trip to a range of mountains whose relative heights are represented by array A, as shown in a figure below. You have to choose how many flags you should take with you. The goal is to set the maximum number of flags on the peaks, according to certain rules.
Flags can only be set on peaks. What's more, if you take K flags, then the distance between any two flags should be greater than or equal to K. The distance between indices P and Q is the absolute value |P − Q|.
For example, given the mountain range represented by array A, above, with N = 12, if you take:
two flags, you can set them on peaks 1 and 5;
three flags, you can set them on peaks 1, 5 and 10;
four flags, you can set only three flags, on peaks 1, 5 and 10.
You can, therefore, set a maximum of three flags in this case.
Write a function:
class Solution { public int solution(int[] A); }
that, given a non-empty array A of N integers, returns the maximum number of flags that can be set on the peaks of the array.
For example, the following array A:
A[0] = 1
A[1] = 5
A[2] = 3
A[3] = 4
A[4] = 3
A[5] = 4
A[6] = 1
A[7] = 2
A[8] = 3
A[9] = 4
A[10] = 6
A[11] = 2
the function should return 3, as explained above.
Assume that:
N is an integer within the range [1..400,000];
each element of array A is an integer within the range [0..1,000,000,000].
Complexity:
expected worst-case time complexity is O(N);
expected worst-case space complexity is O(N) (not counting the storage required for input arguments).
I walk through the solution provided below,
public static int solution(int[] A) {
int N = A.length;
/*
* P = [1, 1, 3, 3, 5, 5, 10, 10, 10, 10, 10, -1]
* */
int[] P = nextPeak(A);
int i = 1;
int result = 0;
while ((i - 1) * i <= N) {
int index = 0;
int flags = 0;
while (index < N && flags < i) {
/*
* P = [1, 1, 3, 3, 5, 5, 10, 10, 10, 10, 10, -1]
* */
index = P[index];
if (index == -1) {
break;
}
flags += 1;
index += i;
}
/*
* maximize the number of flags for the whole segment
* */
result = Math.max(result, flags);
i++;
}
return result;
}
/*
* A = [1, 1, 3, 3, 5, 5, 10, 10, 10, 10, 10, -1]
* */
public static int[] nextPeak(int[] P) {
int N = P.length;
ArrayList<Integer> peaks = new ArrayList<Integer>();
for (int i = 1; i < P.length - 1; i++) {
if (P[i] > P[i - 1] && P[i] > P[i + 1]) {
peaks.add(i);
}
}
int[] A = new int[N];
A[N - 1] = -1;
for (int i = N - 2; i >= 0; i--) {
if (peaks.contains(i)) {
A[i] = i;
} else {
A[i] = A[i + 1];
}
}
return A;
}
Generally, I understand the computation but fail to see where do we meet the condition if you take K flags, then the distance between any two flags should be greater than or equal to K.
I imagine this is inside the while condition of (i-1)*i <= N but unable to comprehend it properly. Would anyone kindly explain it to me?
Your answer is index += i; combined with the condition flags < i in the while loop.
They work the solution in reverse: walking K steps at a time, insert at most K flags.

Add the consecutive digits of a birthdate recursively till it reaches to a single digit

I am developing a numerology application which has to provide a result which is similar to the following,
1 5 0 8 1 9 9 4
6 5 8 9 1 1 1
1 1 1 1 2 2
2 2 2 3 4
4 4 5 7
8 9 1
1 1
2
It has to add the consecutive digits and retain the first digit if the sum is of 2 digits.
I am missing something. Adding a while loop for the length of intList doesn't seem to work.
int date;
List<Integer> sumList = new ArrayList<Integer>();
Scanner s = new Scanner(System.in);
System.out.println("Enter the date");
date = s.nextInt();
int len = Integer.toString(date).length();
int[] convertarray = new int[len];
for (int index = 0; index < len; index++) {
convertarray[index] = date % 10;
date /= 10;
}
List<Integer> intList = new ArrayList<Integer>();
for (int i : convertarray) {
intList.add(i);
}
Collections.reverse(intList);
System.out.println(intList);
int sum = 0;
int size = intList.size();
for (int i = 0; i < intList.size() - 1; i++) {
sum = intList.get(i) + intList.get(i + 1);
int length = (int) (Math.log10(sum) + 1);
if (length > 1) {
int firstDigit = Integer.parseInt(Integer.toString(sum).substring(0, 1));
sum = firstDigit;
}
System.out.print(sum + " ");
sumList.add(sum);
}
System.out.println("\n");
intList.clear();
intList = sumList;
My output is something like,
1 5 0 8 1 9 9 4
6 5 8 9 1 1 1
A simple recursive solution:
public static void main(String[] args) throws Exception {
String birthday = "01091995";
int[] digits = Arrays.stream(birthday.split("")).mapToInt(Integer::parseInt).toArray();
recursiveFunction(digits);
}
private static void recursiveFunction(int[] digits) {
if(digits.length == 1) {
// Base Case
System.out.println(digits[0]);
} else {
// Recursive Case
System.out.println(Arrays.toString(digits));
int[] digitsProcessed = new int[digits.length -1];
for (int i = 0; i < digits.length - 1; i++) {
digitsProcessed[i] = digits[i] + digits[i+1]; // Logic
}
recursiveFunction(digitsProcessed);
}
}
This produces:
[0, 1, 0, 9, 1, 9, 9, 5] // 8 numbers
[1, 1, 9, 10, 10, 18, 14] // 7 numbers
[2, 10, 19, 20, 28, 32] // 6 numbers
[12, 29, 39, 48, 60] // 5 numbers
[41, 68, 87, 108] // 4 numbers
[109, 155, 195] // 3 numbers
[264, 350] // 2 numbers
614 // 1 number
Adding a while loop for the length of intList doesn't seem to work.
Well it can be done with loops, but it would be harder and messier.
An algorithm with recursion would be the following:
Init the array of integers.
Call the recursive function "F" with the array.
From now, the recursive function behaviour:
Check if the recieved array's length is 1.
If it is, print the element and terminate.
If it is not:
Print the recieved array.
Make a new array.
Put in this new array the result of processing the recieved one adding as intended.
Call the recursive function "F" with this new array.

How to count all possible cases?

For example I have array with length n=3:
for(int i = 0; i < n; i++) {
array[i] = i;
}
So the cases should be:
1. 0
2. 1
3. 2
4. 0 1
5. 0 2
6. 1 2
7. 0 1 2
So the number of cases should be 7 for n = 3.
In my code:
int n = 3;
int[] array = new int[n];
for (int i = 0; i < n; i++) {
array[i] = i;
}
int sum = 0;
for (int i = 0; i < n; i++) {
System.out.println(array[i] + " ");
sum++;
for (int j = i; j < n; j++) {
System.out.print(array[j] + " ");
}
System.out.println();
sum++;
}
System.out.println("sum = " + sum);
Output is:
0
0 1 2
1
1 2
2
2
sum = 6
The number 2 is two times so it is wrong and sum is actually = 5. And I don't get cases
4. 0 1
and
5. 0 2
How to count all possible cases?
Sets, not arrays
The first important observance is that you are not using fixed length arrays here but sets of different lengths.
Take a look at your example. You allow
0
1
2
0, 1
0, 2
1, 2
which are not all of size 3.
Also you don't differentiate between
0, 1
1, 0
so order doesn't matter, like in sets.
Power set
That's why you're actually describing power sets here. For the example set {0, 1, 2} its power set is defined as
P({0, 1, 2}) = {
{}, // empty set
{0},
{1},
{2},
{0, 1},
{0, 2},
{1, 2},
{0, 1, 2}
}
Fortunately there exists an easy closed formula for their size. If n is the size of the input set the size of the power set is
2^n
But they also count the empty set, so you will need to -1 if you don't want that:
2^n - 1
Solution
Thus in Java you could write
int Set<Integer> input = ...
int size = (int) Math.pow(2, input.size()) - 1;
and that's all, you don't need to build the contents manually.
But if you're curious and want to build them, take a look at questions like Obtaining a powerset of a set in Java. It's an implementation of the recursive formula shown at Wikipedia.
So, totally inefficient but also working:
int Set<Integer> input = ...
// Build the power-set using the method from linked question
Set<Set<Integer>> power = powerSet(input);
int size = power.size() - 1;

Categories