У меня есть массив чисел размера n, я выбираю все возможные различные возрастающие подпоследовательности массива, для каждой подпоследовательности нахожу побитовое значение ИЛИ и возвращаю их как результат в порядке возрастания.
Пример:
arr= [4,2,4,1], n = 4
Результат
[1,2,4,6]
Объяснение:
All possible distinct increasing subsequences are:
[4]
[2]
[2, 4]
[1]
Bit wise or values are:
[4]
[2]
[2 | 4] = [6]
[1]
So values are [2,4,6, 1]
sorted result is [1,2,4,6]
Другой пример:
arr = [3,2,4,6], n = 4
Результат
[2,3,4,6,7]
Объяснение:
All increasing subsequences and corresponding bitwise or values are:
[3] : 3
[3, 4] : 7
[3, 4, 6] : 7
[3, 6] : 7
[2] : 2
[2, 4] : 6
[2, 4, 6] : 6
[2, 6] : 6
[4] : 4
[4, 6] : 6
[6] : 6
So distinct values are [3,7,2,6,4]
sorted result is [2,3,4,6,7]
Ограничения:
1 <= n <= 10^4
1 <= arr[i] <= 1024
Я решил это, используя грубую силу, вот мой код:
import java.util.*;
public class Main {
public static void main(String[] args) {
int[] arr = {3,2,4,6};
List<List<Integer>> subsequences = findIncreasingSubsequences(arr);
Set<Integer> set = new TreeSet<>();
for (List<Integer> subsequence : subsequences) {
int or = subsequence.get(0);
for(int i=1; i<subsequence.size(); i++) {
or |= subsequence.get(i);
}
//System.out.println(subsequence + " : "+ or);
set.add(or);
}
System.out.println("Result : " + set);
}
public static List<List<Integer>> findIncreasingSubsequences(int[] arr) {
List<List<Integer>> result = new ArrayList<>();
List<Integer> current = new ArrayList<>();
findSubsequences(arr, result, current, 0);
return result;
}
private static void findSubsequences(int[] arr, List<List<Integer>> result, List<Integer> current, int start) {
if (!current.isEmpty()) {
result.add(new ArrayList<>(current)); // Add current subsequence to result
}
for (int i = start; i < arr.length; i++) {
if (current.isEmpty() || arr[i] > current.get(current.size() - 1)) {
current.add(arr[i]);
findSubsequences(arr, result, current, i + 1);
current.remove(current.size() - 1);
}
}
}
}
Как решить эту проблему за меньшее время.
@cyberbrain Это не возрастающая подпоследовательность (относительно значений, а не индексов).
@Unmitigated, спасибо, пропустил это, но во втором примере действительно ли {3,4} является возрастающей подпоследовательностью? Между этими числами во входных данных есть 2, так что для меня это скорее комбинация, чем подпоследовательность? Последовательность будет означать, что все элементы во входном списке расположены рядом - для меня...
@cyberbrain Подпоследовательности не являются смежными. Они отличаются от подмассивов. См. Википедию: «В математике подпоследовательность данной последовательности — это последовательность, которую можно получить из данной последовательности путем удаления некоторых элементов или их отсутствия без изменения порядка остальных элементов».
Я думаю, не обойтись без того, чтобы не попробовать все возможные комбинации. Но, конечно, вы можете удалить множество ветвей, если они не дадут нового результата.
Вот мой код. У меня такой же подход, как у вас, но я на самом деле не рассчитывал все комбинации, а просто передал значение or-ed для этой текущей комбинации. И сокращение выполняется, если принятие следующего элемента не даст вам нового или заданного значения, которое вы видели раньше.
import java.util.*;
class Solution {
void solve(List<Integer> nums, int index, int lastIndex, int taken, int curr, Set<Integer> values) {
if (taken > 0) {
values.add(curr);
}
if (index >= nums.size()) {
return;
}
// Take it.
if (lastIndex == -1 || nums.get(index) > nums.get(lastIndex)) {
int newOrValue = curr | nums.get(index);
if (!values.contains(newOrValue)) {
solve(nums, index + 1, index, taken + 1, newOrValue, values);
}
}
// Leave it
solve(nums, index + 1, lastIndex, taken, curr, values);
}
public List<Integer> getIncreasingSubsequenceOrValues(List<Integer> nums) {
Set<Integer> values = new HashSet<>();
solve(nums, 0 /* index */, -1 /* lastIndex */, 0 /* taken */, 0 /* curr */, values);
return new ArrayList<>(values);
}
}
public class Main {
static void print(List<Integer> v) {
for (int el : v) {
System.out.print(el + " ");
}
System.out.println();
}
static void test(List<Integer> nums) {
System.out.print("Testing for array: ");
print(nums);
Solution solution = new Solution();
List<Integer> result = solution.getIncreasingSubsequenceOrValues(nums);
print(result);
}
public static void main(String[] args) {
test(Arrays.asList(4, 2, 4, 1));
test(Arrays.asList(3, 2, 4, 6));
}
}
Можно запустить по адресу: https://ideone.com/NmfuVV
Поскольку все элементы массива находятся в диапазоне от 1 до 1024 (210), максимальное значение побитового ИЛИ равно 2047. Эту проблему можно решить в O(2047N)
с помощью динамического программирования. Для каждого возможного значения побитового ИЛИ поддерживайте минимально возможное значение, которым может закончиться подпоследовательность с этим побитовым значением ИЛИ (чтобы разрешить как можно больше возрастающих подпоследовательностей).
public static List<Integer> solve(int... nums) {
final int MAX_VAL = 1024, MAX_OR = MAX_VAL | MAX_VAL - 1;
var minEndVal = new int[MAX_OR + 1];
for (int i = 1; i <= MAX_OR; i++) minEndVal[i] = MAX_VAL + 1;
for (int num : nums)
for (int i = 0; i < MAX_OR; i++)
if (minEndVal[i] < num)
minEndVal[i | num] = Math.min(minEndVal[i | num], num);
return IntStream.rangeClosed(1, MAX_OR).filter(i -> minEndVal[i] <= MAX_VAL).boxed().toList();
}
Спасибо, не могли бы вы объяснить подробнее if (minEndVal[i] < num) minEndVal[i | num] = Math.min(minEndVal[i | num], num);
, а также то, как он учитывает только возрастающие комбинации подпоследовательностей?
@Sid minEndVal[i]
хранит минимальное значение (которое было замечено до сих пор), которое может быть последним элементом возрастающей подпоследовательности, побитовое ИЛИ которой равно i
. Если minEndVal[i]
меньше текущего элемента, то мы можем просто добавить текущее число в конец предыдущей возрастающей подпоследовательности и сформировать более длинную возрастающую подпоследовательность. Нам нужно поддерживать minEndVal[i]
, обновляя минимальное значение, чтобы позже можно было добавить больше элементов в подпоследовательность.
Думаю, я не правильно понял утверждение - по крайней мере мои результаты отличаются от ваших примеров. Что вы подразумеваете под «подпоследовательностью»? Разве {2,4,1} не является подпоследовательностью {4,2,4,1}? Потому что для этого побитовое значение или будет равно 7, и вы не включили это в результат вашего примера № 1.