Graph traversal adding too many items - java

I'm trying to do graph traversal for partitions where the values decrease and are at least two items in length. For example the partitions where n = 6 would be [3, 2, 1], [4, 2], [5, 1]. Something in the way I am counting these is flawed, but I haven't been able to find a solution. Each number has an instance of the class Node stored in items the node class has 2 properties, value and next. Next is an ArrayList of nodes which the current node can be broken up into. For example, 3 would have the following node:
Node(3, [Node(3, [Node(2), Node(1])])
I am currently getting the value 4294 when n = 10, where it should be only 9.
public static int countItems(int n, ArrayList<Integer> notAllowed) {
if (memo.containsKey(n)) {
return memo.get(n);
}
int total = 0;
for (int i = n - 1; i > 1; i--) {
ArrayList<Integer> localNotAllowed = new ArrayList<>(notAllowed);
int x = n - i;
int count = 0;
if (!localNotAllowed.contains(x) && i > x) {
count++;
localNotAllowed.add(x);
}
Node node = items.get(x);
for (Node item : node.next) {
count += findWeight(item.value, localNotAllowed);
}
memo.put(x, count);
total += count;
}
return total;
}
countItems(10, new ArrayList<Integer>() {{add(n);}});

Related

Get the longest route traveled in a graph

I have an array of nodes that are connected to each other
I have below network of nodes. Here 0 is the starting point, I want to travel as many nodes as possible with a node traveled only once.
Also during a trip from 0 to destination node, I want to have only a single odd numbered node (like 1, 3, 5, 7).
Now I need to find out the longest route I can travel from my beginning position 0.
Example :
int[] array = { 0, 9, 0, 2, 6, 8, 0, 8, 3, 0 };
In above graph, below are possibilities:
0 -> 6 -> 4 (valid path, length = 3 nodes)
0 -> 9 -> 1 (Not valid path, length as we have 2 odd numbers here 1 & 9)
0 -> 2 -> 3 -> 8 (valid path, length = 4 nodes)
0 -> 2 -> 3 -> 8 -> 5 (Not valid path as we have 2 odd numbers here 3 & 5)
0 -> 2 -> 3 -> 8 -> 7 (Not valid path as we have 2 odd numbers here 3 & 7)
So the answer is 4 for this input.
Below is the program I am trying.
public int process(int[] array) {
int count = array.length;
ArrayList<Integer>[] branches = new ArrayList[count];
for (int i = 0; i < count; i++) {
branches[i] = new ArrayList<>();
}
int begin = 0;
for (int i = 0; i < count; i++) {
if (array[i] != i) {
branches[i].add(array[i]);
branches[array[i]].add(i);
}
}
Arrays.stream(branches).forEach(System.out::println);
Queue<Network> networkQueue = new LinkedList<Network>();
ArrayList<Integer> networkList = branches[begin];
networkList.forEach(value -> {
Network net = new Network(0, value);
networkQueue.add(net);
});
System.out.println("printing starting nodes.......");
List<Network> nodes = new ArrayList<>();
for (Network n : networkQueue) {
nodes.add(n);
System.out.println(n.value + " : " + n.road);
}
int result = 0;
return result;
}
class Network {
int road, value;
public Network(int road, int value) {
this.road = road;
this.value= value;
}
}
The program prints the branches and the nodes for the starting point i.e 0 :
[2, 6, 9]
[9]
[0, 3]
[2, 8]
[6]
[8]
[4, 0]
[8]
[5, 7, 3]
[1, 0]
printing starting nodes.......
2 : 0
6 : 0
9 : 0
I got stuck on finding the longest route, how to proceed next with this program, please help me here.
This is a classic Depth First Search with backtracking problem.
The gist of this algorithm is as follow.
Starting from the start node, visit all its neighbors that have not been visited and do not break the max odd number node of 1 restriction. Add the current node to the current path and increment odd number node counter if the current node number is odd. Do this recursively until you've exhausted all valid paths for one neighbor, then backtrack for the remaining neighbors.
The following is the implementation with your provided input as the test case. I also added another list of list variable that is called res. This will give you all the valid longest path. I used a map to represent a graph, but you can modify this as you like.
import java.util.*;
public class LongestRoute {
private static int maxLen = 0;
private static List<List<Integer>> res = new ArrayList<>();
public static int longestRouteWithRestrictions(Map<Integer, List<Integer>> graph, int startNode) {
Set<Integer> visited = new HashSet<>();
visited.add(startNode);
List<Integer> path = new ArrayList<>();
path.add(startNode);
dfs(graph, startNode, visited, startNode % 2 == 1 ? 1 : 0, path);
return maxLen;
}
private static void dfs(Map<Integer, List<Integer>> graph, int currentNode, Set<Integer> visited, int oddNumNodeCnt, List<Integer> path) {
if(path.size() > maxLen) {
maxLen = path.size();
res.clear();
res.add(new ArrayList<>(path));
}
else if(path.size() == maxLen) {
res.add(new ArrayList<>(path));
}
for(int neighbor : graph.get(currentNode)) {
if(visited.contains(neighbor) || oddNumNodeCnt == 1 && neighbor % 2 != 0) {
continue;
}
path.add(neighbor);
visited.add(neighbor);
dfs(graph, neighbor, visited, oddNumNodeCnt + (neighbor % 2 != 0 ? 1 : 0), path);
path.remove(path.size() - 1);
visited.remove(neighbor);
}
}
public static void main(String[] args) {
//Init a test graph
Map<Integer, List<Integer>> graph = new HashMap<>();
Integer[] neighbors_0 = {2,6,9};
List<Integer> list0 = Arrays.asList(neighbors_0);
graph.put(0, list0);
Integer[] neighbors_1 = {9};
List<Integer> list1 = Arrays.asList(neighbors_1);
graph.put(1, list1);
Integer[] neighbors_2 = {0,3};
List<Integer> list2 = Arrays.asList(neighbors_2);
graph.put(2, list2);
Integer[] neighbors_3 = {2,8};
List<Integer> list3 = Arrays.asList(neighbors_3);
graph.put(3, list3);
Integer[] neighbors_4 = {6};
List<Integer> list4 = Arrays.asList(neighbors_4);
graph.put(4, list4);
Integer[] neighbors_5 = {8};
List<Integer> list5 = Arrays.asList(neighbors_5);
graph.put(5, list5);
Integer[] neighbors_6 = {0,4};
List<Integer> list6 = Arrays.asList(neighbors_6);
graph.put(6, list6);
Integer[] neighbors_7 = {8};
List<Integer> list7 = Arrays.asList(neighbors_7);
graph.put(7, list7);
Integer[] neighbors_8 = {5,7};
List<Integer> list8 = Arrays.asList(neighbors_8);
graph.put(8, list8);
Integer[] neighbors_9 = {0,1};
List<Integer> list9 = Arrays.asList(neighbors_9);
graph.put(9, list9);
System.out.println(longestRouteWithRestrictions(graph, 0));
for(List<Integer> route : res) {
System.out.println(route.toString());
}
}
}
I'm sorry I don't have time to code it, but here is the logic i would apply.
starting from 0 the program generate linked lists of neighbors. In our case:
[0->2]
[0->9]
[0->6]
checking neighbors (last elements in lists): if they are odd then increment a counter that refer to that path list.
If the odd counter ==2 then erase that list from further analsys
for each list start again from 1. using last element as input. When no more VALID lists can be generated find the one with longest path, counting elements.
Just pay attention that a valid neighbor cannot be the same as the previous element in the list to avoid infinite loops: The only valid list generable by [0->2] it's [0->2->3], and not [0->2->0]
You can try below approach, which should be relatively simple to implement as well as track. Firstly, you need to form a Node class which would have information about 3 things:
Max length of the path traveled so far while visiting all the nodes until this node from node 0, visiting each node just once.
A variable called oddCounter that counts the number of odd nodes encountered so far in this path.
A boolean variable isVisited to know if this node is already visited or not.
Now just implement BFS with nodes being as instance of this type of class defined above and while doing BFS, you just need to update each node variables accordingly.
Please note that you need to do BFS to all nodes starting from node 0, each time resetting the values of above 3 variables so you can keep track of the further paths in that route through this node. You can terminate the loop beyond certain nodes if you have already found one odd node. This way it would be more flexible in future as well where you might want to include maybe 2 or 3 odd numbered nodes in your path as compared to just one node currently.
Also, you would need to create a resultList and currList where you create a new currList everytime you traverse to a new node and update resultList with currList if length of currList is greater than the length of resultList as per the constraints we have.
I am sure that this approach can be optimized further using Dynamic Programming. As you know the route length and number of odd nodes until a given node say i, you can just do a BFS now from ith node and using memoization to keep track of previous route length and odd numbered nodes, which we are already tracking using above class we created.
this is in c++, but i think it works the same:
#include <vector>
using namespace std;
void dfs(vector<vector<int>> &graphs, int nextidx, int& ticket, int& cities);
int solution(vector<int>& T)
{
if (T.empty()) return 0;
vector<vector<int>> graphs(T.size());
int ticket = 1;
int citiesmax = 0;
for (int i = 1; i < T.size(); ++i) {
graphs[T[i]].emplace_back(i);
}
// 0-> 2, 6, 9
// 1-> _
// 2-> 3
// 3-> 8
// 4-> _
// 5-> _
// 6-> 4
// 7-> _
// 8-> 5, 7
// 9-> 1
for (int i = 0; i < graphs[0].size(); ++i)
{
int nextidx = graphs[0][i];
int cities = 1;
dfs(graphs, nextidx, ticket, cities);
if(cities > citiesmax)
{
citiesmax = cities;
}
}
return citiesmax;
}
void dfs(vector<vector<int>> &graphs, int nextidx, int &ticket, int& cities)
{
if (graphs[nextidx].empty()) return;
for (int i = 0; i < graphs[nextidx].size(); ++i)
{
if (graphs[nextidx][i] % 2 > 0)
{
if (ticket == 0) return;
ticket--;
}
int idx = graphs[nextidx][i];
cities++;
dfs(graphs, idx, ticket, cities);
}
return;
}
int main()
{
vector<int> test1 = {0,9,0,2,6,8,0,8,3,0};
int res = solution(test1);
}

Heapify with ArrayList

I wrote this method to test my priority queue, which has as parameter an ArrayList and a Comparator:
#Test
public void testPriorityQueue_ExtractMax() {
PriorityQueue queue = new PriorityQueue(new IntegerComparator());
Integer[] arrayExp={14, 9};
queue.insert(16);
queue.insert(9);
queue.insert(14);
queue.extractMax();
assertArrayEquals(arrayExp, queue.getArray().toArray());
}
Now, if I execute it, it says that the first elements in my result is 9, but I has to be 14 (the new root of my queue). These are the methods extract max and heapify. How can I solve it?
public void heapify(int index) {
int largest = index;
int leftIndex = 2 * index + 1;
int rightIndex = 2 * index + 2;
if (leftIndex < queue.size() && c.compare(queue.get(index), (queue.get(leftIndex))) < 0)
largest = leftIndex;
if (rightIndex < queue.size() && c.compare(queue.get(largest), queue.get(rightIndex)) < 0)
largest = rightIndex;
if (largest != index) {
swap(index, largest);
heapify(largest);
}
}
public T extractMax() {
if (queue.size() == 0) return null;
T min = queue.get(0);
queue.remove(0);
queue.set(0, queue.get(queue.size() - 1));
heapify(0);
return min;
}
This is the IntegerComparator:
public class IntegerComparator implements Comparator<Integer>{
#Override
public int compare(Integer l1, Integer l2) {
int result = l1.compareTo(l2);
if(result != 0)
return result;
return -1;
}
}
EDIT_2: This is my insert method, can it be the problem:
public void insert(T elem) {
int i = queue.size();
int parentIndex = (i - 1) / 2;
while (i > 0 && c.compare(elem, queue.get(parentIndex)) == 1) {
queue.set(i, queue.get(parentIndex));
i = parentIndex;
parentIndex = (i - 1) / 2;
}
queue.add(i, elem);
}
As a suggestion, consider adding an unused element at index 0 so that accessing the parents/children of each node is more intuitive.
Example:
Consider the heap heap = [-1, 6, 4, 5, 2, 1, 4, 3] where the root is defined as heap[1], and the data for the heap is located from index = 1 to the heap's size.
To access the children of a node at index, it is intuitive to say that the left child is defined as heap[2 * index] and the right child is defined as heap[2 * index + 1]. Similarly, to access the parent node of a node at index, one can use int truncation to access parents:
int parentInd = (int)(index/2);
T parent = heap[parentInd];
Solution:
As raul1ro has pointed out, you are losing the data from index 0 that you were not intending to remove.
In extractMax():
T min = queue.get(0);
queue.remove(0);
queue.set(0, queue.get(queue.size() - 1));
should be:
T min = queue.get(0); //Get min
T temp = queue.get(queue.size() - 1); //Get the last element in heap as temp
queue.remove(queue.size - 1); //Remove the last element
queue.set(0, temp); //Set the root to the temp value so that you can heapify
This will make it so that you only lose 1 element when you extractMax()
Hope that helps!
When you do this queue.remove(i); automatically shift all elements, after index i, to left with one position.
https://www.tutorialspoint.com/java/util/arraylist_remove.htm
And with queue.set(0, queue.get(queue.size() - 1)); You just set value of last index from queue on index 0 and lose value from index 0, but it still remain on last position.
After reading insert method
At queue.set(i, queue.get(parentIndex)); if i = queue.size() in first step, that mean index i don't exist in queue.

Using binary search to count number of elements smaller/larger than a given number

Given a number current, find the number of values in an array which are larger and smaller than that value.
//sort array for binary search
int[] digits = Arrays.stream(sc.nextLine()
.split(" "))
.mapToInt(Integer::parseInt)
.sorted()
.toArray();
//for duplicate values, find higher index of current.
while(low <= high){
int mid = low + (high - low)/2;
if(digits[mid] > current){
high = mid - 1;
}else if (digits[mid] == current){
startindex = mid;
high = mid - 1;
}else{
startindex = mid;
low = mid +1;
}
}
//for duplicate values, find lower index of current.
int endindex = -1;
low = 0;
high = no_digits - 1;
while(low <= high){
int mid = low + (high - low)/2;
if(digits[mid] > current){
high = mid - 1;
}else if (digits[mid] == current){
endindex = mid;
low = mid + 1;
}else{
endindex = mid;
low = mid + 1;
}
}
System.out.println(endindex + "-" + startindex);
if(digits[0] > current){
smallest = 0;
largest = no_digits;
System.out.println(String.format("Smaller: %d, Greater: %d", smallest, largest));
} else if (digits[no_digits - 1] < current){
smallest = no_digits;
largest = 0;
System.out.println(String.format("Smaller: %d, Greater: %d", smallest, largest));
}else {
smallest = startindex;
largest = no_digits - endindex - 1;
System.out.println(String.format("Smaller: %d, Greater: %d", smallest, largest));
}
}
}
Sample input:
5 8 7 2 4 3 7 9 1 9 - Array of ints.
7
0
100
3
6
Output:
Smaller: 5, Greater: 3
Smaller: 0, Greater: 10
Smaller: 10, Greater: 0
Smaller: 2, Greater: 7
Smaller: 5, Greater: 5
My results:
6-5 //start and end index.
Smaller: 5, Greater: 3
-1--1
Smaller: 0, Greater: 10
9-9
Smaller: 10, Greater: 0
2-2
Smaller: 2, Greater: 7
4-4
Smaller: 5, Greater: 4
I managed to come out with the above algorithm which accounts for values larger or lower than any value in the array.
However, I am unable to find a solution to account for values that are nonexistent in the array without iterating though the array since I need to accomplish the above in O((N+Q) log N) time.
In this case, this would be the last test case where the value is 6. 6 does not exist in the array but I will still need to count all values higher/lower than 6.
Binary search algorithm produces the "insertion point" for values that do not exist in the array. Your startIndex and endIndex would give you the first "eligible" item, or the one right next to it. In other words, if you are looking for all values less than 6, the search for endpoint would yield the index of 5.
Note that you don't need to roll your own binary search algorithm: Java provides an implementation for you.
Reference: Arrays.binarySearch
EDIT The question has been edited, now it contains an additional requirement that the algorithm should work fast for multiple queries, more precisely: the overall runtime should be O((N + Q) * log(N)) where N is the size of the array and Q is the number of queries.
The approach below works only for Q = 1.
I don't see any reason not to do it in linear O(N) time.
// get this from scanner
int number = 5;
int[] array = {6, 2, 7, 4, 1, 42};
// the "algorithm"
int numLessThan = 0;
int numGreaterThan = 0;
for (int i: array) {
if (i < number) numLessThan++;
if (i > number) numGreaterThan++;
}
System.out.println(
"Num greater than: " + numGreaterThan + " " +
"Num less than: " + numLessThan
);
Output:
Num greater than: 3 Num less than: 3
If you insist on doing it with streams:
long numLessThan = Arrays.stream(array).filter(x -> x < number).count();
long numGreaterThan = Arrays.stream(array).filter(x -> x > number).count();
Even though it traverses the array twice, it is still O(N).
Since you use a Stream anyway, with a map-call no less, you're iterating the whole array anyway.
So just do
class Counters {
AtomicInteger smaller = new AtomicInteger(0);
AtomicInteger larger = new AtomicInteger(0);
private final int upperLimit;
private final int lowerLimit;
public Counters(int up, int down) {
upperLimit = up;
lowerLimit = down;
}
public void consider(int value) {
if (value > upperLimit) larger.incrementAndGet();
if (value < lowerLimit) smaller.incrementAndGet();
}
public int getSmaller() { return smaller.get(); }
public int getLarger() { return larger.get(); }
}
Counters c = new Counters(upper, lower);
IntStream.of(yourValues).parallel().forEach(c::consider);
// your output here
System.out.printf("Smaller: %d - Larger: %d", c.getSmaller(), c.getLarger());
or a more generic version
class MatchCounter<T> {
AtomicInteger count = new AtomicInteger(0);
private final Predicate<T> match;
public MatchCounter(Predicate<T> m) { match = m; }
public void consider(T value) {
if (m.test(value)) { count.incrementAndGet(); }
}
public int getCount() { return count.get(); }
}
MatchCounter<Integer> smaller = new MatchCounter<>(i -> i < lower);
MatchCounter<Integer> larger = new MatchCounter<>(i -> i > upper);
Consumer<Integer> exec = smaller::consider;
Stream.of(yourArray).parallel().forEach(exec.andThen(larger::consider));
System.out.printf("Smaller: %d - Larger: %d", smaller.getCount(), larger.getCount());
See Arrays which would come handy here.
void stats(int[] a, int sought) {
a = Arrays.copyOf(a, a.length);
Arrays.sort(a);
int index = Arrays.binarySearch(a, sought);
int smaller, larger;
if (index < 0) {
// Not found.
index = ~index; // Insertion position.
smaller = index;
larger = index:
} else {
// Found.
smaller = index;
while (smaller > 0 && a[smaller] == sought) {
--smaller;
}
while (index <= 0 && a[index] == sought) {
++index;
}
}
larger = a.length - index;
int equals = index - smaller;
System.out.printf("Smaller %d, equal %d, larger %d.%n",
smaller, equals, larger);
}
As you see, when finding an element, it would suffice to loop back O(N) which is less than sorting O(N log N).
Faster - O(log N) instead of O(N) for that part - would be if one could do a binary search on sought - 0.5 and sought + 0.5.
void stats(int[] a, int sought) {
a = Arrays.copyOf(a, a.length);
for (int i = 0; i < a.length; ++i) {
a[i] *= 2;
}
Arrays.sort(a);
int smallerI = Arrays.binarySearch(a, 2 * sought - 1);
int largerI = Arrays.binarySearch(a, 2 * sought + 1);
int smaller = ~smallerI;
int larger = a.length - ~largerI;
int equals = ~largerI - ~smallerI;
System.out.printf("Smaller %d, equal %d, larger %d.%n",
smaller, equals, larger);
}
This uses doubled integers, which has the drawback that the valid domain of array values is halved.
In your case your own binary search algorithm should opt for this latter case (without doubling), using an implicit sought + 0.5, never finding, looking for an insertion position.
Okay, so after your edit you state you want to run several queries over the same array so preparation time is less important.
To do that, build a red-black tree from the array; that will give you a sorted structure that allows a search in O(log N).
So what you do for the "smaller" count is go to the left until you find a node with a value equal or larger than the lower limit; count all left children of that. Analogue for the larger (go to the right, find equal or smaller, count to the right).
It won't matter if the item is not present in the array because you're looking for an "equal-or-larger" so if e.g. 6 is not present but you find a 5, you'll count from there - only you add 1 to the count.
You just have to filter and then count occurences. For example :
public static void main(String[] args) {
int[] values = {5, 8, 7, 2, 4, 3, 7, 9, 1, 9};
printCount(values, 7);
printCount(values, 0);
printCount(values, 100);
printCount(values, 3);
printCount(values, 6);
}
private static void printCount(int[] values, int value) {
long smallerCount = Arrays.stream(values).filter(v -> v < value).count();
long largerCount = Arrays.stream(values).filter(v -> v > value).count();
System.out.println(String.format("Smaller : %d, Larger: %d", smallerCount, largerCount));
}

Need explanation for algorithm searching minimal large sum

I'm solving Codility questions as practice and couldn't answer one of the questions. I found the answer on the Internet but I don't get how this algorithm works. Could someone walk me through it step-by-step?
Here is the question:
/*
You are given integers K, M and a non-empty zero-indexed array A consisting of N integers.
Every element of the array is not greater than M.
You should divide this array into K blocks of consecutive elements.
The size of the block is any integer between 0 and N. Every element of the array should belong to some block.
The sum of the block from X to Y equals A[X] + A[X + 1] + ... + A[Y]. The sum of empty block equals 0.
The large sum is the maximal sum of any block.
For example, you are given integers K = 3, M = 5 and array A such that:
A[0] = 2
A[1] = 1
A[2] = 5
A[3] = 1
A[4] = 2
A[5] = 2
A[6] = 2
The array can be divided, for example, into the following blocks:
[2, 1, 5, 1, 2, 2, 2], [], [] with a large sum of 15;
[2], [1, 5, 1, 2], [2, 2] with a large sum of 9;
[2, 1, 5], [], [1, 2, 2, 2] with a large sum of 8;
[2, 1], [5, 1], [2, 2, 2] with a large sum of 6.
The goal is to minimize the large sum. In the above example, 6 is the minimal large sum.
Write a function:
class Solution { public int solution(int K, int M, int[] A); }
that, given integers K, M and a non-empty zero-indexed array A consisting of N integers, returns the minimal large sum.
For example, given K = 3, M = 5 and array A such that:
A[0] = 2
A[1] = 1
A[2] = 5
A[3] = 1
A[4] = 2
A[5] = 2
A[6] = 2
the function should return 6, as explained above. Assume that:
N and K are integers within the range [1..100,000];
M is an integer within the range [0..10,000];
each element of array A is an integer within the range [0..M].
Complexity:
expected worst-case time complexity is O(N*log(N+M));
expected worst-case space complexity is O(1), beyond input storage (not counting the storage required for input arguments).
Elements of input arrays can be modified.
*/
And here is the solution I found with my comments about parts which I don't understand:
public static int solution(int K, int M, int[] A) {
int lower = max(A); // why lower is max?
int upper = sum(A); // why upper is sum?
while (true) {
int mid = (lower + upper) / 2;
int blocks = calculateBlockCount(A, mid); // don't I have specified number of blocks? What blocks do? Don't get that.
if (blocks < K) {
upper = mid - 1;
} else if (blocks > K) {
lower = mid + 1;
} else {
return upper;
}
}
}
private static int calculateBlockCount(int[] array, int maxSum) {
int count = 0;
int sum = array[0];
for (int i = 1; i < array.length; i++) {
if (sum + array[i] > maxSum) {
count++;
sum = array[i];
} else {
sum += array[i];
}
}
return count;
}
// returns sum of all elements in an array
private static int sum(int[] input) {
int sum = 0;
for (int n : input) {
sum += n;
}
return sum;
}
// returns max value in an array
private static int max(int[] input) {
int max = -1;
for (int n : input) {
if (n > max) {
max = n;
}
}
return max;
}
So what the code does is using a form of binary search (How binary search works is explained quite nicely here, https://www.topcoder.com/community/data-science/data-science-tutorials/binary-search/. It also uses an example quite similar to your problem.). Where you search for the minimum sum every block needs to contain. In the example case, you need the divide the array in 3 parts
When doing a binary search you need to define 2 boundaries, where you are certain that your answer can be found in between. Here, the lower boundary is the maximum value in the array (lower). For the example, this is 5 (this is if you divide your array in 7 blocks). The upper boundary (upper) is 15, which is the sum of all the elements in the array (this is if you divide the array in 1 block.)
Now comes the search part: In solution() you start with your bounds and mid point (10 for the example).
In calculateBlockCount you count (count ++ does that) how many blocks you can make if your sum is a maximum of 10 (your middle point/ or maxSum in calculateBlockCount).
For the example 10 (in the while loop) this is 2 blocks, now the code returns this (blocks) to solution. Then it checks whether is less or more than K, which is the number of blocks you want. If its less than K your mid point is high because you're putting to many array elements in your blocks. If it's more than K, than your mid point is too high and you're putting too little array elements in your array.
Now after the checking this, it halves the solution space (upper = mid-1).
This happens every loop, it halves the solution space which makes it converge quite quickly.
Now you keep going through your while adjusting the mid, till this gives the amount blocks which was in your input K.
So to go though it step by step:
Mid =10 , calculateBlockCount returns 2 blocks
solution. 2 blocks < K so upper -> mid-1 =9, mid -> 7 (lower is 5)
Mid =7 , calculateBlockCount returns 2 blocks
solution() 2 blocks < K so upper -> mid-1 =6, mid -> 5 (lower is 5, cast to int makes it 5)
Mid =5 , calculateBlockCount returns 4 blocks
solution() 4 blocks < K so lower -> mid+1 =6, mid -> 6 (lower is 6, upper is 6
Mid =6 , calculateBlockCount returns 3 blocks
So the function returns mid =6....
Hope this helps,
Gl learning to code :)
Edit. When using binary search a prerequisite is that the solution space is a monotonic function. This is true in this case as when K increases the sum is strictly decreasing.
Seems like your solution has some problems. I rewrote it as below:
class Solution {
public int solution(int K, int M, int[] A) {
// write your code in Java SE 8
int high = sum(A);
int low = max(A);
int mid = 0;
int smallestSum = 0;
while (high >= low) {
mid = (high + low) / 2;
int numberOfBlock = blockCount(mid, A);
if (numberOfBlock > K) {
low = mid + 1;
} else if (numberOfBlock <= K) {
smallestSum = mid;
high = mid - 1;
}
}
return smallestSum;
}
public int sum(int[] A) {
int total = 0;
for (int i = 0; i < A.length; i++) {
total += A[i];
}
return total;
}
public int max(int[] A) {
int max = 0;
for (int i = 0; i < A.length; i++) {
if (max < A[i]) max = A[i];
}
return max;
}
public int blockCount(int max, int[] A) {
int current = 0;
int count = 1;
for (int i = 0; i< A.length; i++) {
if (current + A[i] > max) {
current = A[i];
count++;
} else {
current += A[i];
}
}
return count;
}
}
This is helped me in case anyone else finds it helpful.
Think of it as a function: given k (the block count) we get some largeSum.
What is the inverse of this function? It's that given largeSum we get a k. This inverse function is implemented below.
In solution() we keep plugging guesses for largeSum into the inverse function until it returns the k given in the exercise.
To speed up the guessing process, we use binary search.
public class Problem {
int SLICE_MAX = 100 * 1000 + 1;
public int solution(int blockCount, int maxElement, int[] array) {
// maxGuess is determined by looking at what the max possible largeSum could be
// this happens if all elements are m and the blockCount is 1
// Math.max is necessary, because blockCount can exceed array.length,
// but this shouldn't lower maxGuess
int maxGuess = (Math.max(array.length / blockCount, array.length)) * maxElement;
int minGuess = 0;
return helper(blockCount, array, minGuess, maxGuess);
}
private int helper(int targetBlockCount, int[] array, int minGuess, int maxGuess) {
int guess = minGuess + (maxGuess - minGuess) / 2;
int resultBlockCount = inverseFunction(array, guess);
// if resultBlockCount == targetBlockCount this is not necessarily the solution
// as there might be a lower largeSum, which also satisfies resultBlockCount == targetBlockCount
if (resultBlockCount <= targetBlockCount) {
if (minGuess == guess) return guess;
// even if resultBlockCount == targetBlockCount
// we keep searching for potential lower largeSum that also satisfies resultBlockCount == targetBlockCount
// note that the search range below includes 'guess', as this might in fact be the lowest possible solution
// but we need to check in case there's a lower one
return helper(targetBlockCount, array, minGuess, guess);
} else {
return helper(targetBlockCount, array, guess + 1, maxGuess);
}
}
// think of it as a function: given k (blockCount) we get some largeSum
// the inverse of the above function is that given largeSum we get a k
// in solution() we will keep guessing largeSum using binary search until
// we hit k given in the exercise
int inverseFunction(int[] array, int largeSumGuess) {
int runningSum = 0;
int blockCount = 1;
for (int i = 0; i < array.length; i++) {
int current = array[i];
if (current > largeSumGuess) return SLICE_MAX;
if (runningSum + current <= largeSumGuess) {
runningSum += current;
} else {
runningSum = current;
blockCount++;
}
}
return blockCount;
}
}
From anhtuannd's code, I refactored using Java 8. It is slightly slower. Thanks anhtuannd.
IntSummaryStatistics summary = Arrays.stream(A).summaryStatistics();
long high = summary.getSum();
long low = summary.getMax();
long result = 0;
while (high >= low) {
long mid = (high + low) / 2;
AtomicLong blocks = new AtomicLong(1);
Arrays.stream(A).reduce(0, (acc, val) -> {
if (acc + val > mid) {
blocks.incrementAndGet();
return val;
} else {
return acc + val;
}
});
if (blocks.get() > K) {
low = mid + 1;
} else if (blocks.get() <= K) {
result = mid;
high = mid - 1;
}
}
return (int) result;
I wrote a 100% solution in python here. The result is here.
Remember: You are searching the set of possible answers not the array A
In the example given they are searching for possible answers. Consider [5] as 5 being the smallest max value for a block. And consider [2, 1, 5, 1, 2, 2, 2] 15 as the largest max value for a block.
Mid = (5 + 15) // 2. Slicing out blocks of 10 at a time won't create more than 3 blocks in total.
Make 10-1 the upper and try again (5+9)//2 is 7. Slicing out blocks of 7 at a time won't create more than 3 blocks in total.
Make 7-1 the upper and try again (5+6)//2 is 5. Slicing out blocks of 5 at a time will create more than 3 blocks in total.
Make 5+1 the lower and try again (6+6)//2 is 6. Slicing out blocks of 6 at a time won't create more than 3 blocks in total.
Therefore 6 is the lowest limit to impose on the sum of a block that will permit breaking into 3 blocks.

Find next highest unique number from the given digits

Given a set of n symbols, a size k, and a combination of length k of non-repeating characters from the symbol set, write only an ITERATIVE algorithm to print the next highest unique number that can be made.
Ex:
Symbols =[1,2,3,4,5]
size = 3;
given combination = 123, result = 124
given combination = 254, result = 312
Here's a pseudocode algorithm to do this:
int n = length(Symbols);
int k = length(A);
// TRACK WHICH LETTERS ARE STILL AVAILABLE
available = sort(Symbols minus A);
// SEARCH BACKWARDS FOR AN ENTRY THAT CAN BE INCREASED
for (int i=k-1; i>=0; --i) {
// LOOK FOR NEXT SMALLEST AVAILABLE LETTER
for (int j=0; j<n-k; ++j) {
if (A[i] < available[j]) {
break;
}
}
if (j < n-k) {
// CHANGE A[i] TO THAT, REMOVE IT FROM AVAILABLE
int tmp = A[i];
A[i] = available[j];
available[j] = tmp;
// RESET SUBSEQUENT ENTRIES TO SMALLEST AVAILABLE
for (j=i+1; i<k; ++j) {
A[j] = available[i+1-j];
}
return A;
} else {
// A[i] MUST BE LARGER THAN AVAILABLE, SO APPEND TO END
available = append(available,A[i]);
}
}
public class IncrementSybmols {
public static void main(String[] args) throws Throwable {
List<Integer> syms = Arrays.asList(1,2,3,4,5);
test(syms, 3, Arrays.asList(1,2,3), Arrays.asList(1,2,4));
test(syms, 3, Arrays.asList(2,5,4), Arrays.asList(3,1,2));
test(syms, 3, Arrays.asList(4,3,5), Arrays.asList(4,5,1));
test(syms, 3, Arrays.asList(5,4,2), Arrays.asList(5,4,3));
test(syms, 3, Arrays.asList(5,4,3), null);
}
private static void test(List<Integer> syms, int n, List<Integer> in, List<Integer> exp) {
List<Integer> out = increment(syms, n, in);
System.out.println(in+" -> "+out+": "+( exp==out || exp.equals(out)?"OK":"FAIL"));
}
private static List<Integer> increment(List<Integer> allSyms, int n, List<Integer> in){
TreeSet<Integer> availableSym = new TreeSet<Integer>(allSyms);
availableSym.removeAll(in);
LinkedList<Integer> current = new LinkedList<Integer>(in);
// Remove symbols beginning from the tail until a better/greater symbols is available.
while(!current.isEmpty()){
Integer last = current.removeLast();
availableSym.add(last);
// look for greater symbols
Integer next = availableSym.higher(last);
if( next != null ){
// if there is a greater symbols, append it
current.add(next);
availableSym.remove(next);
break;
}
}
// if there no greater symbol, then *shrug* there is no greater number
if( current.isEmpty() )
return null;
// fill up with smallest symbols again
while(current.size() < n){
Integer next = availableSym.first();
availableSym.remove(next);
current.add(next);
}
return current;
}
}
When you are iterating (backwards) across the digits you do not have to check the lowest available every time, instead you can check the last checked digit versus the current, if it is less, skip to the next digit while adding the current to available, if it is more then check the available to find the lowest(higher than current) possible and fill in the rest with lowest from queue.
i.e. 254
current = 4 // 4 < 1,3 so no higher available
last_checked = 4 // available = {1, 3, 4}
current = 5 // 4 < 5 so no higher available
last_checked = 5 // available = {1, 3, 4, 5}
current = 2 // 5 > 2 so search available for lowest possible(higher than 2) = 3
set 3,_,_ // Then just add lowest elements until full: 3,1,2 = 312
This way you only have to look at the available symbols once, and you are only comparing at most k times.
Try this method out:
public int nextCombo(int[] symbols, int combo, int size) {
String nc = "";
symbols = java.util.Arrays.sort(symbols);
for (int i = 0; i < size; i++) nc += Integer.toString(symbols[symbols.length - 1]);
if (Integer.parseInt(nc) == combo) return combo; //provided combo is the largest possible so end the method
nc = "";
int newCombo = 0;
while (newCombo < combo) { //repeat this process until the new combination is greater than the provided one
for (int i = 0; i < size; i++) { //keep appending numbers from the symbol array onto the new combo until the size limit is reached
nc += Integer.toString(symbols[(int) Math.floor(Math.random() * size)]);
}
newCombo = Integer.parseInt(nc);
}
return newCombo;
}

Categories

Resources