I have a list of names.
I want to partition this list into groups of a specified size. All groups should be equal to or less than the specified size, with an equal as possible group size across the groups, and as close to the specified size as possible.
What algorithm (Java-esque pseudo code if possible please!) determines the most appropriate group sizes?
For example:
List contains 13 names - max team size 3.
Output (group sizes): 3, 3, 3, 2, 2
List contains 13 names - max team size 4.
Output: 4, 3, 3, 3
List contains 31 names - max team size 5.
Output: 5, 5, 5, 4, 4, 4, 4
List contains 31 names - max team size 6.
Output: 6, 5, 5, 5, 5, 5
List contains 31 names - max team size 10.
Output: 8, 8, 8, 7
It's pretty straightforward. You calculate the result of N div M and add 1 to have the correct number of arrays (N is the list length and M the max team size), then you iterate on all arrays to add an item until you run out of items
example : 43 names, max team number 4 => 43 mod 4 + 1 = 11, remains 3. so 11 arrays (10 with 4 and 1 with 3)
I'm not going to write code for this, but
If the list size is a multiple of the max team size, divide by team size to get the number of groups, all of max size, and stop.
Integer-divide the list size by the max team size, then add one. That's the number of groups.
Subtract the list size from that next higher multiple; that's the number of teams that are one less than the max size.
This obviously only works for inputs that are close to working out; it fails if the max team size is large compared to the size of the list.
If the number of items in the list is N and the max number of items in sublist is K, the solution of your problem comes from solving a Linear Diophantine Equation
K*x + (K-1)*y = N
with additional constraints
x > 0
y >= 0
Where x is the number of sublists of the exact size K, and y is the number of sublists of length K-1.
EDIT : Because of the coefficients to the equation are always off by one from each other, the solution is rather straightforward:
int m = (N+K-1)/K;
int x = N - (K-1)*m; // Number of "full" lists
int y = K*M - N; // Number of off-by-one lists
Example 1:
N = 31, K = 5
m = (31+5-1)/5 = 7
x = 31 - (5-1)*7 = 3 // 3 lists of 5 items
y = 5*7 - 31 = 4 // 4 lists of 4 items
Example 2:
N = 32, K = 4
m = (32+4-1)/4 = 8
x = 32 - (4-1)*8 = 8 // 8 lists of 4 items
y = 4*8 - 32 = 0 // no lists of 3 items
Look up an algorithm for solving linear Diophantine equations on the net - it is not that complicated, if you understand Euclidean algorithm well. The number of solutions of the equation without constraints is infinite, but once you add the constraints, you should get a single solution.
public class Q {
public static void q(int size, int maxTeamSize) {
int numOfTeams = size / maxTeamSize;
int mod = size % maxTeamSize;
numOfTeams += (mod > 0) ? 1 : 0;
System.out.print("\n(" + size + ":" + maxTeamSize + ")");
for (int i = 0; i < numOfTeams; i++) {
System.out.print(" " + (size / numOfTeams + ((i < mod) ? 1 : 0)));
}
}
public static void main(String[] args) {
q(13, 3);
q(12, 4);
q(31, 5);
q(31, 6);
}
}
Related
Hey guys I will explain the question below.
keys = list of integers
I have to find max degree of divisibility of elements in keys. But When I calculate the degree of divisibility I should just consider the keys element. for example:
keys = [2,4,8,2]
2 = [2,2] degree of divisibility is 2
4 = [2,4,2] degree of divisibility is 3
8 = [2,4,8,2] degree of divisibility is 4 so we choose 8 with 4 degrees of divisibility.
after that we have to calculate
if maxDegreeOfDivisibility(4 in our case) * 10^5 < validityPeriod* instructionCount then its
true.
and we return 1 and 4*10^5. I hope I explain the question if u guys have any questions about the question :D I can answer.
public static List<Integer> encryptionValidity(int instructionCount, int validityPeriod,
List<Integer> keys) {
List<Integer> result = Arrays.asList(0, 0);
Map<Integer, Integer> degreeOfDivisibilityCache = new HashMap<>();
for (int i: keys) {
Integer degreeOfDivisibility = degreeOfDivisibilityCache.getOrDefault(i, -1);
if (degreeOfDivisibility == -1) {
int count = 0;
for (int j : keys) {
if (j > 0 && i % j == 0) {
count++;
}
}
degreeOfDivisibilityCache.put(i, count);
}
}
int maxDegreeOfDivisibility = degreeOfDivisibilityCache.values().stream()
.max(Comparator.comparingInt(Integer::intValue)).orElse(0);
int s = maxDegreeOfDivisibility * 10000;
result.set(1, s);
BigInteger ic = BigInteger.valueOf(instructionCount);
BigInteger a = ic.multiply(BigInteger.valueOf(validityPeriod));
if (a.compareTo(BigInteger.valueOf(s)) > 0) {
result.set(0, 1);
}
return result;
}
Declare 32 buckets, each signifying an integer's most significant bit
Scan through the keys, record their occurrence count and put unique instances in their respective buckets (unsorted), and flag as candidate
Scan the buckets, in descending order of the most significant bit it represents. Compare only with contents of buckets of lower bits.
3.1 On a divisible match, aggregate current count with occurrence count in step 2 & clear that number's candidacy flag
3.2 If current count > current record holder, set record holder = this key & current count
Example: [19, 4, 12, 6, 4, 3, 8]
Occurrences: {19: 1, 4: 2, 12: 1, 6: 1, 3: 1, 8: 1}
Bucket #16: [19*]
Bucket #8: [12*, 8*]
Bucket #4: [4*, 6*]
Bucket #2: [3*]
Scan 19 => 1(self); set record: (19, 1)
Scan 12 => 1(self) + 2(from 4) + 1(from 6) + 1(from 3) = 5; clear 4, 6, 3; set record: (12, 5)
Scan 8 => 2
Skip 4, 6, 3
Time Complexity: if n is total integer count and m is number of unique integers, O(n) + O(m^2) in BigO notation, but actually O(n) + C*(m^2)/3 (for some constant C)
Analysis:
First scan in step 1: O(n)
Bucket scan: Worst case (full buckets) is inverted binary tree.
This is an infinite series that sums to 1/3 (See related Wikipedia articles: here and here)
Credits to Rick James for candidate flag idea.
Sort the numbers, largest first. Flag each one as being a candidate.
For each number that is still a candidate:
2.1. Count how many of the numbers after it divide into it. Mark each such number as no longer a candidate.
Search the list for the largest count from step 2.1. That count is the Degree of Divisibility of the original list.
Example:
[2,4,8,2,3,6] (before sorting)
[8*,6*,4*,3*,2*,2*] -- '*' means "is a candidate"
check 8: [8*,6*,4,3*,2,2] -- divisible by 4 (8,4,2,2)
check 6: [8*,6*,4,3,2,2] -- divisible by 4 (6,3,2,2)
the rest don't need checking
The answer is 4.
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)
I have an array of numbers, now I want to find all the consecutive subarray sums whose value matches with less than or equal to k. I want to return the largest subarray size.
This is my program:
public static int process(List<Integer> arr, int k) {
int size = 0;
for (int i = 0; i < arr.size(); i++) {
int sum = 0;
for (int j = i; j < arr.size(); j++) {
sum += arr.get(j);
if (sum <= k) {
size = Math.max(size, j - i + 1);
}
}
}
return size;
}
constraints:
arr size is between 1 to 100000
array elements are between 1 to 100
k is between 1 to 1000000
Explanation:
Arrays.asList(2, 3, 5, 1, 1, 2, 1), 7
arr = 2, 3, 5, 1, 1, 2, 1
k= 7
possible sub arrays:
[2], [3], [5], [1], [1], [2], [1]
[2,3], [5,1], [1,1], [1,2], [2,1]
[5,1,1], [1,1,2], [1,2,1]
[1,1,2,1]
Maximum sub array is [1,1,2,1] = its length is 4. So program should return 4.
I got this task in a competitive exam last week, when I used this code it has passed only 50% test cases. It failed for some hidden test cases saying wrong output, and some test cases saying time out.
What is the issue with my code?
Update:
Modified my code little bit. And added a sample example.
Nested loops means that performance is O(n2). You need to re-think the algorithm.
Below is an O(n) solution, which I will show by example. Writing the code is still your challenge.
What we need is a way to know the sum of values before a particular index. With that we can calculate sumRange(i, j) = sumBefore(j) - sumBefore(i).
Since we're looking for sumRange(i, j) = k, we need to check if we have a sumBefore(i) = sumBefore(j) - k. If we do, we have a candidate range with size = j - i.
So, iterate the values and calculate the accumulated sum. Build a Map of accSum to indexAfter the value leading to that accumulated sum.
Say the array is [1, 6, 5, 3, 2, 8, 4, 2, 7, 1] and k = 13:
Index: 0 1 2 3 4 5 6 7 8 9
Value: 1 6 5 3 2 8 4 2 7 1
accSum: 0 1 7 12 15 17 25 29 31 38 39 sumBefore
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
indexAfter: 0 1 2 3 4 5 6 7 8 9 10 index
Adding the dummy 0 → 0 mapping just simplifies the logic of your code.
As you iterate, you have the accumulated sum up to now, inclusive, i.e. sumBefore(i + 1), so look to see if we have a range, i.e. sumBefore(x) = sumBefore(i + 1) - k = accSum - k, so look in the map for accSum - k, and if found, the value is x, meaning we have a candidate range of x, i+1.
I hope that all made sense.
UPDATE
The question was changed to look for sums <= k, not just sums exactly equal to k.
This is easily done with the logic above by changing the Map to a TreeMap, or more specifically a NavigableMap, and replace the get() call with a ceilingEntry() call:
Returns a key-value mapping associated with the least key greater than or equal to the given key, or null if there is no such key.
If the returned key (sum) is greater than the parameter, the result is a subarray sum that is less than k.
Given an int array of length n, split up the array into 3 parts and make sure that the 2 smaller parts are as large as possible.
Splitting rules:
pick two indices a and b of the given array (0 <= a <= b <= n-1)
Size of first part is: sum of all array entries from index 0 to a-1 (inclusive)
Size of second part is: sum of all array entries from index a to b (inclusive)
Size of third part is: sum of all array entries from b+1 to n-1 (inclusive)
empty parts are possible..
The expected output is the sum of the 2 smaller parts (their sizes).
Example, an array ofn = 6 and some values are given.
The solution calculates a = 2, b = 3 which splits the array into 3 parts: left part is of size 6 + 7 = 13, middle part is 8 + 9 = 17, right part is 4 + 5 = 9. Output is 13 + 9 = 22 (sum of the 2 smaller parts).
Graphical representation:
More examples:
[6, 8, 3, 5, 7, 2, 4, 6] should be split up into:
Left (6 + 8 = 14)
Middle (3 + 5 + 7 = 15)
Right (2 + 4 + 6 = 12)
Output is 14 + 12 = 26 (sum of the 2 smaller parts)
[9, 12, 4, 7, 10, 2, 5, 8, 11, 3] should be split up into:
Left (9 + 12 + 4 = 25)
Middle (7 + 10 + 2 + 5 = 24)
Right (8 + 11 + 3 = 22)
Output is 22 + 24 = 46 (sum of the 2 smaller parts)
My approach doesn't work for the given test cases:
// L is size of left part, M is size of middle part, R is size of right part
/* I start with all array entries in the middle part, then I put elements
out of the middle part into the left and right part (depending on which
is smaller) until one of them is larger than M, this approach works for
many cases, two exceptions are the first 2 arrays given as examples in
this post.
*/
long a = 1;
long b = n;
long L = R = 0;
long M = arr.sumOfAllArrayEntries;
long temp;
long[] arr = {9, 12, 4, 7, 10, 2, 5, 8, 11, 3};
while (M > Math.max(L, R)) {
if (L < R) {
// move leftmost element of M to L
temp = arr[(int) a++];
M -= temp;
L += temp;
}
else {
// move rightmost element of M to R
temp = arr[(int) b--];
M -= temp;
R += temp;
}
}
// finds maximum of M, L, R
temp = Math.max(M, Math.max(L, R));
// finds 2 smallest numbers out of M, L, R
if (temp == M)
temp = L + R;
else if (temp == L)
temp = M + R;
else if (temp == R)
temp = M + L;
// temp is equal to the sum of the 2 smaller parts
System.out.println("Output: " + temp);
The basic idea that comes to mind:
Loop over all positions for a.
Loop over all positions for b.
Calculate the left, mid and right sum.
Calculate the target sum and store this if it's better than the best sum we've seen.
This can be optimised to O(n) by noting a few things:
For any given position b, the best position for a will always be, sum-wise, in the middle of b and the start (specifically at the point that minimises the difference between the left and mid sums).
The best position for a can't move left as b moves right (since that will decrease left sum for a bigger mid sum, decreasing the target sum).
This means we only need one loop over b, while keeping track of a as we go, increasing it when appropriate.
We can keep track of the sums as we go.
This gives us the following code:
int arr[] = {9, 12, 4, 7, 10, 2, 5, 8, 11, 3};
int sum = 0;
for (int i: arr)
sum += i;
int a = 0;
int left = 0, mid = 0;
int best = 0;
for (int b = 0; b < arr.length; b++)
{
mid += arr[b];
// since this loop increases `a` with every iteration, and `a` never resets,
// it will not run more than O(n) times in total
while (a < b && Math.min(left + arr[a], mid - arr[a]) > Math.min(left, mid))
{
left += arr[a];
mid -= arr[a];
a++;
}
int right = sum - mid - left;
best = Math.max(best,
mid + left + right - Math.max(mid, Math.max(left, right)));
}
System.out.println(best);
Live demo.
The problem with your approach is when you get into a situation like:
a b
6 7 | 8 9 | 4 5
L=13 M=17 R=9
M > Math.max(L, R) will be true, so you'll move one of the elements, despite already having the best split.
Note how I did Math.min(left + arr[a], mid - arr[a]) > Math.min(left, mid) in my code instead of simply left < mid. You will need something similar to check whether you should continue.
An example you'd need to consider is one where you need to further increase the bigger side:
100 | 10 120 | 90 -> 100 10 | 120 | 90
That might complicate your code quite a bit more.
This can be done using concept of Two Pointers.So first see main array as concatenation of 3 sub-arrays. A,B and C. Now we can first calculate total sum of all elements of array which indicates the considered array has all the elements.
So now we need to keep track of summation of 3 continuous subarrays of the original array. Consider here that we have 3 arrays here as
A ---> Starting from the left-side (index 0)
B ---> Middle sub-array
C ---> Starting from the right-side (index n-1)
Here answer should be min(sumOfA,min(sumOfB,sumOfC)) which is to be maximized.
Here we have stored summation of all elements in sub-array B considering it has all elements of array. A and C are empty. Now we will remove one by one element from either of the end and add that value to appropriate sub-array A or C and we need to delete it from the B by subtracting it.
Now the question remains that which element is to be removed. For this we will check value of A and C and whoever has lower sum than other one, we will add elements from that end to the specific sub-array.
Another problem here may arise is Termination Condition. Here termination condition would be Sum of B > Sum of A && Sum of B > Sum of C. So when sum of B becomes lesser than any of the other two sub arrays, we need to stop there.
Complexity of this approach : O(n)
Code :
import java.util.*;
class Main
{
public static void main(String args[])
{
long arr[]={9, 12, 4, 7, 10, 2, 5, 8, 11, 3};
long sumOfA=0;
long sumOfB=0;
long sumOfC=0;
int a = 0; //set end of sub-array A
int b = arr.length-1; //set start of sub-array-C
long maximum =0; // Minimum of sum of all subarrays should be maximum,
// That will be sufficient to get the answer
long answer=0;
int answer_a=0;
int answer_b=0;
for(int i=0;i<arr.length;i++)
{
sumOfB+=arr[i];
}
for(int i=0;i<arr.length;i++)
{
long minimum = Math.min(sumOfA , Math.min(sumOfB,sumOfC));
if(minimum>=maximum)
{
answer_a=a;
answer_b=b;
ArrayList<Long> list=new ArrayList<Long>(); //To calculate the answer
list.add(sumOfA);
list.add(sumOfB);
list.add(sumOfC);
Collections.sort(list);
answer=Math.max(answer,list.get(0)+list.get(1)); //take minimum two elements
maximum=minimum;
}
if(sumOfB < sumOfC || sumOfB < sumOfA)
break;
if(a>=b) //If both pointer passes to each other
break;
if(sumOfA == sumOfC)
{
if(arr[a]<arr[b]) //take minimum element
{
sumOfA+=arr[a];
sumOfB-=arr[a];
a++; //move a to next element
}
else
{
sumOfC+=arr[b];
sumOfB-=arr[b];
b--; //move b to prev element
}
}
else if(sumOfA > sumOfC)
{
sumOfC+=arr[b];
sumOfB-=arr[b];
b--;
}
else
{
sumOfA+=arr[a];
sumOfB-=arr[a];
a++;
}
}
System.out.println("a(exclsive) : "+answer_a);
System.out.println("b(exclsive) : "+answer_b);
System.out.println("Answer : "+answer);
}
}
Answer for [9, 12, 4, 7, 10, 2, 5, 8, 11, 3] :
a(exclsive) : 3
b(exclsive) : 6
Answer : 46
Answer for [6, 8, 3, 5, 7, 2, 4, 6] :
a(exclsive) : 2
b(exclsive) : 4
Answer : 26
So as part of the Vector class I'm trying to create, I also want the ability for the user to be able to shift the elements in the array an 'n' number of places depending on what is specified. If the user inputs a number that is larger than the array size, then the elements continue shifting back to the start and moving. An example would be:
1 2 3 4 (shifted 1) => 4 1 2 3
1 2 3 4 (shifted 4) => 1 2 3 4
1 2 3 4 (shifted 5) => 4 1 2 3
I don't have much code so far except:
public Vector shifted(int amount) {
Vector vectorShifted = new Vector(length);
for (int i = 0; i < length; i++);
vectorShifted.elements[i] = this.elements[i + amount]
}
return vectorShifted;
}
However, when I run this program and a number greater than length is entered, an error is displayed. Is there a way of modifying this code in that any number, positive or negative can be inputted and shift the values across?
Just like lazary2 said, you can use the modulo operator %
Change:
vectorShifted.elements[i] = this.elements[i + amount]
to vectorShifted.elements[i] = this.elements[(i + amount) % length]
If you want to use Array:
Integer[] array = {0,1,2,3,4};
Collections.rotate(Arrays.asList(array), 3);
System.out.println(Arrays.toString(array)); //[2, 3, 4, 0, 1]