Scratch grouping is a subsequence with a given sum with lexicographic priority

I am looking for a way to search for a subsequence in a given sequence that sums up to a given number ( sum , here 4 ) with lexicographic priority.

Take for example the following example:

 1,2,2,4,1,1 

Different subsequences can add up to 4 . For example, 1,2,1 , 2,2 2,1,1 . If there are several such sequences, the lexicographic first of the corresponding index array should be returned: therefore, if you can find such a sequence with the first element, you need to return it, if not, for the second and therefore one (iterative (take the next)) and recursively (after selecting the first, the next, but first should be closest to the head of the sequence).

So, for this example, we choose 1,2,1 . Now there are 2,4,1 left. If we repeat this problem, we cannot make a match with 2 : 2,4 more than 4 , and 2,1 less than 4 . So we choose 4 . Finally, we must select 2 and 1 .

The practical application of this concept is the rollercoaster line-up. You need 4 people for a walk, but some people in groups with their friends would like everyone to be on the same trip together.

In this example, 1 is one person at the front of the line, 2 is a group of friends 2 behind him. Now we need only 4 people for this trip, and we already have 3 , so we cut the line ( 2 and 4 ) and take the first individual person, which gives us only 4 people.

+6
source share
3 answers

If I understand the problem correctly, then what you are basically trying to do is group numbers so that the sum is 4 , and you give priority to adding numbers to the queue first.

You can do this using a dynamic programming approach. I am using int[] and int as a sum here, but the problem can be generalized to work with most datastructures.

First you must define a comparator that compares index lists, for example, lexicographical:

 public class LexComp<T extends Comparable<T>> implements Comparator<List<T>> { @Override public int compare (List<T> la, List<T> lb) { Iterator<T> ita = la.iterator(); Iterator<T> itb = lb.iterator(); while(ita.hasNext() && itb.hasNext()) { T ea = ita.next(); T eb = itb.next(); int cr = ea.compareTo(eb); if(cr != 0x00) { return cr; } } if(itb.hasNext()) { return 1; } else if(ita.hasNext()) { return -1; } return 0; } } 

Further you can use the following method:

 public ArrayList<Integer> groupSum (int[] values, int sum) { ArrayList[] memory = new ArrayList[sum+1]; memory[0] = new ArrayList<Integer>(); LexComp<Integer> lc = new LexComp<Integer>(); int index = 0; for(int val : values) { for(int i = sum-val; i >= 0 ; i--) { if(memory[i] != null) { ArrayList<Integer> tmp = (ArrayList<Integer>) memory[i].clone(); tmp.add(index); if(memory[i+val] == null || lc.compare(tmp,(ArrayList<Integer>) memory[i+val]) < 0) { memory[i+val] = tmp; } } } index++; } return memory[sum]; } 

This method returns ArrayList<Integer> indexes whose corresponding elements will be summed up to sum and null if such a group cannot be created. It will give priority to some groups according to the LexComp comparator.

For this input:

 groupSum(new int[] {1,2,2,4,1,1},4); groupSum(new int[] {1,2,3,2,2,2},4); groupSum(new int[] {1,2,2,3},4); groupSum(new int[] {1,2,2,3,1},4); 

This leads to:

 [0, 1, 4] [0, 2] [0, 3] [0, 1, 4] 

So, you must select the first, second and fifth elements that really add up to 4 . Then you have to remove these elements from the array yourself and repeat the process. In the event that such a sum cannot be built or there are not enough elements to sum up to 4 - as mentioned earlier - the algorithm will return null . In this case, you need to invent a backup mechanism. Perhaps the return of the group differs from sum smallest.

Background

This is a dynamic programming approach. You create a memory that stores - for each sum - the best solution found. Initially, we did not see any values, so all elements contain null except memory[0] , which contains an empty arraylist (because the sum of the null elements is 0 ). Thus, the memory looks like this:

 Mem 4 -> null 3 -> null 2 -> null 1 -> null 0 -> [] 

The algorithm now iterates over the values. The first value we come across as an example is 1 . Now we are looking for lists that are already defined, and a single memory[0] . We can update this list in the list [0] (storage indexes of arrays), the sum of which is expressed in 1 . Since at this moment the value for this list is null , there is no alternative, so we add this list to memory[1] :

 Mem 4 -> null 3 -> null 2 -> null 1 -> [0] 0 -> [] 

Next element 2 : we can update the two lists [] -> [1] and [0] -> [1] , this will lead to lists with the sums of 2 and 3 respectively, so we save them by these memory indices:

 Mem 4 -> null 3 -> [0,1] 2 -> [1] 1 -> [0] 0 -> [] 

The next item will again be 2 . Now we can update lists 4 : [] -> [2] , [0] -> [0,2] , [1] -> [1,2] and [0,1] -> [0,1,2] . The first problem is that the sum [0,1,2] is 5 , which is higher than sum . This is not interesting, so we throw this one. However, the problem is that some of the places already contain lists:

 Mem 4 -> null 3 -> [0,1] <> [0,2] 2 -> [1] <> [2] 1 -> [0] 0 -> [] 

For conflicting lists, we need to look for permission. In this case, the comparator - in this case, LexComp fixes the errors. Since we do this lexicographically, [0,1] benefits from [0,2] and [1] from [2] . After permission, the lists look like this:

 Mem 4 -> [3] 3 -> [0,1] 2 -> [1] 1 -> [0] 0 -> [] 

The next element is 4 . The only list we can update so that the sum is less than or equal to sum is [] -> [3]

 Mem 4 -> [3] 3 -> [0,1] 2 -> [1] 1 -> [0] 0 -> [] 

Next item 1 . We can update all lists except one 4 -> [3] (otherwise the sum will be more than 4 ). But again, this leads to many conflicts:

 Mem 4 -> [3] <> [0,1,4] 3 -> [0,1] <> [1,4] 2 -> [1] <> [0,4] 1 -> [0] <> [4] 0 -> [] 

Now, if we run a lexicographic comparator, it will sometimes accept new lists, and sometimes old lists. After permission, the memory looks like this:

 Mem 4 -> [0,1,4] 3 -> [0,1] 2 -> [0,4] 1 -> [0] 0 -> [] 

Now our current best solution for generating a group that sums up to four has changed from [3] to [0,1,4] . Finally, the last element 1 will not greatly change the game:

 Mem 4 -> [0,1,4] <> [0,1,5] 3 -> [0,1] <> [0,4,5] 2 -> [0,4] <> [0,5] 1 -> [0] <> [5] 0 -> [] 

What after permission reads:

 Mem 4 -> [0,1,4] 3 -> [0,1] 2 -> [0,4] 1 -> [0] 0 -> [] 

Now we have examined all the elements and the best solution for generating 4 is memory[4] or [0,1,4] .

Different order

This approach can be generalized in the sense that providing another Comparator on a List<T> (here LexComp<T> ) will give priority to another array of indices. The comparator should always fulfill at least the transitivity constraint: if x is less than y and y is less than z: x must be less than z. In addition, the list of indexes will always increase. Thus, an array of indices [4,1,0] impossible.

+4
source

The correct answer to this question largely depends on how you determine your priorities.

Should we always choose the first group in the row, if possible, or is it the optimal solution to have as many people from the front of the line?

those. given

 1, 2, 2, 3, 3, 4, 2, 2, 3, 1 

- optimal solution

1, 2, 1

or

13

To get started, here's a recursive solution that does the first:

 private static List<Integer> getSumIndices(int sum, List<Integer> queue) { return getSumIndices(sum, new ArrayList<>(queue), 0); } private static List<Integer> getSumIndices(int sum, List<Integer> queue, int offset) { System.out.printf("Looking for sum %s in values of %s with offset %s%n", sum, queue, offset); if(sum == 0) { //Base case return new ArrayList<>(); } for(int i = 0; i < queue.size(); i++) { int value = queue.get(i); // Can we actually use this group if(value <= sum) { try { // See if we can find the remainder if we use this group ArrayList<Integer> list = new ArrayList<>(); list.add(i + offset); list.addAll(getSumIndices(sum - value, queue.subList(i + 1, queue.size()), offset + i + 1)); return list; } catch(IllegalArgumentException e) { // We couldn 't, continue looking } } } // We could not construct the sum using the values in the queue System.out.printf("Failed to construct sum %s from values in %s%n", sum, queue); throw new IllegalArgumentException(String.format("Could not construct sum %s from values in %s%n", sum, queue)); } 

Results:

 q=[1, 2, 2, 3, 3, 4, 2, 2, 3, 1] Looking for sum 4 in values of [1, 2, 2, 3, 3, 4, 2, 2, 3, 1] with offset 0 Looking for sum 3 in values of [2, 2, 3, 3, 4, 2, 2, 3, 1] with offset 1 Looking for sum 1 in values of [2, 3, 3, 4, 2, 2, 3, 1] with offset 2 Looking for sum 0 in values of [] with offset 10 Index: Group Size 0: 1 1: 2 9: 1 Remaining q=[2, 3, 3, 4, 2, 2, 3] q=[1, 2, 3, 2, 3, 4, 2, 2, 3, 2] Looking for sum 4 in values of [1, 2, 3, 2, 3, 4, 2, 2, 3, 2] with offset 0 Looking for sum 3 in values of [2, 3, 2, 3, 4, 2, 2, 3, 2] with offset 1 Looking for sum 1 in values of [3, 2, 3, 4, 2, 2, 3, 2] with offset 2 Failed to construct sum 1 from values in [3, 2, 3, 4, 2, 2, 3, 2] Looking for sum 0 in values of [2, 3, 4, 2, 2, 3, 2] with offset 3 Index: Group Size 0: 1 2: 3 Remaining q=[2, 2, 3, 4, 2, 2, 3, 2] q=[1, 2, 2] Looking for sum 4 in values of [1, 2, 2] with offset 0 Looking for sum 3 in values of [2, 2] with offset 1 Looking for sum 1 in values of [2] with offset 2 Failed to construct sum 1 from values in [2] Looking for sum 1 in values of [] with offset 3 Failed to construct sum 1 from values in [] Failed to construct sum 3 from values in [2, 2] Looking for sum 2 in values of [2] with offset 2 Looking for sum 0 in values of [] with offset 3 Index: Group Size 1: 2 2: 2 Remaining q=[1] q=[2, 3, 3] Looking for sum 4 in values of [2, 3, 3] with offset 0 Looking for sum 2 in values of [3, 3] with offset 1 Failed to construct sum 2 from values in [3, 3] Looking for sum 1 in values of [3] with offset 2 Failed to construct sum 1 from values in [3] Looking for sum 1 in values of [] with offset 3 Failed to construct sum 1 from values in [] Failed to construct sum 4 from values in [2, 3, 3] Could not construct sum 4 from values in [2, 3, 3] 
+3
source

You can scroll through the list and add it in order as long as it is larger than the value you are looking for.

code:

 public static int addListValues(int[] list, int num){//Returns number which can not be added by anything else in the list to be <= num. boolean b[] = new boolean[list.length];//List of numbers already taken care of. True for not, false for cleared. for(int i = 0; i < b.length; i++){ b[i] = true; } int count = 0;//Amount of numbers in int[] list which have been added to equal less than or equal to num. int total = 0; while(true){//loops until values left can not be added to equal or be less than num. int check = 0; for(int i = 0; i < list.length; i++){//Loops through list. if(b[i]){//If the number has not been added already. System.out.println("CHECKING: " + i); if(total + list[i] > num){//Adds to check if the number is greater than num. check++; } if(total + list[i] <= num){//Adds numbers together to equal num or less than num. System.out.println("TEST: " + list[i] + " TOTAL: " + total); if(total + list[i] != num){ boolean contains = false; int index = 0; for(int o = 0; o < list.length; o++){ if(list[o] == num - total && b[o] && o != i){ contains = true; index = o; break; } } if(contains){ System.out.println("1: " + index + ", " + list[index]); b[index] = false; count++; total = 0; }else{ System.out.println("2"); b[i] = false; count++; total+= list[i]; } }else{ System.out.println("3"); b[i] = false; count++; total = 0; } }else if(check == list.length - count){//Check if "check" is equal to the amount left over. In other words, if the numbers left are higher than the number you are looking for. System.out.println("FINAL: 3"); int t = 0; for(int j = 0; j < list.length; j++){ if(b[j]){ t += list[j]; } } return t;//More than one number is left and is higher than num. Returns numbers left added together }else if(count == list.length-1){ System.out.println("FINAL: 2"); return list[i];//returns list[i] if it is the only number left over. } }else if(count >= list.length){ System.out.println("FINAL: 1"); return total;//Returns total if there is nothing left over. The total may be anything less than the "num". } } } } 

I tested this method with multiple sets of numbers and it works. I did not know what to return if there was more than one value left and was above 4, so I added the values ​​on the left and returned this value.

This code does not require import.

+1
source

Source: https://habr.com/ru/post/987835/


All Articles