Я пытаюсь найти алгоритм для следующей проблемы:
У меня есть набор триплетов целых чисел - позвольте назвать эти целые числа A, B, C. Значение, хранящееся внутри, может быть большим, поэтому вообще невозможно создать массив размером A, B или C. Цель состоит в том, чтобы минимизировать размер коллекции. Для этого нам предоставлено простое правило, которое позволяет нам объединять триплеты:
- Для двух триплетов (A, B, C) и (A ', B', C ') удалите исходные триплеты и поместите триплет (A | A', B, C), если B == B 'и C = C ', где | побитовое ИЛИ. Аналогичные правила выполняются и для B и C.
Другими словами, если два значения двух триплетов равны, удалите эти два триплета, побитовые ИЛИ третьи значения и поместите результат в коллекцию.
Жадный подход обычно вводит в заблуждение в подобных случаях, и поэтому для этой проблемы я не могу найти простой контрпример, который привел бы к правильному решению. Для списка из 250 элементов, где правильное решение равно 14, средний размер, вычисленный жадным слиянием, составляет около 30 (варьируется от 20 до 70). Субоптимальные накладные расходы становятся больше по мере увеличения размера списка.
Я также пытался играть со множеством бит, но я не нашел значимых результатов. Только очевидный факт: если записи уникальны (что можно с уверенностью предположить), счетчик бит всегда увеличивается.
Здесь глупая жадная реализация (это просто концептуальная вещь, пожалуйста, не учитывайте стиль кода):
public class Record {
long A;
long B;
long C;
public static void main(String[] args) {
List<Record> data = new ArrayList<>();
// Fill it with some data
boolean found;
do {
found = false;
outer:
for (int i = 0; i < data.size(); ++i) {
for (int j = i+1; j < data.size(); ++j) {
try {
Record r = merge(data.get(i), data.get(j));
found = true;
data.remove(j);
data.remove(i);
data.add(r);
break outer;
} catch (IllegalArgumentException ignored) {
}
}
}
} while (found);
}
public static Record merge(Record r1, Record r2) {
if (r1.A == r2.A && r1.B == r2.B) {
Record r = new Record();
r.A = r1.A;
r.B = r1.B;
r.C = r1.C | r2.C;
return r;
}
if (r1.A == r2.A && r1.C == r2.C) {
Record r = new Record();
r.A = r1.A;
r.B = r1.B | r2.B;
r.C = r1.C;
return r;
}
if (r1.B == r2.B && r1.C == r2.C) {
Record r = new Record();
r.A = r1.A | r2.A;
r.B = r1.B;
r.C = r1.C;
return r;
}
throw new IllegalArgumentException("Unable to merge these two records!");
}
Есть ли у вас какие-либо идеи, как решить эту проблему?