Участник:Irdkwmnsb
Специализация алгоритма для генерации следующего разбиения на подмножества
Рассмотрим множество первых n натуральных чисел:
Упорядочим все разбиения на множества
лексикографически. Для этого, во-первых, в каждом разбиении упорядочим множества лексикографически. Будем говорить, что подмножество лексикографически меньше подмножества , если верно одно из следующих условий:- существует такое, что , , для всех если и только если , и существует такое что ;
- и для всех и \ .
Разбиения упорядочены лексикографически следующим образом. Разбиение
лексикографически меньше разбиения если существует такое , что .
Рассмотрим алгоритм нахождения лексикографически следующего разбиения на подмножества:
- Будем хранить подмножества в списке списков, например, разбиение будет выглядеть так:
1 | 2 | 3 |
4 | 5 |
- Будем поддерживать массив удалённых элементов — элементы, которые затем нужно будет вернуть в разбиение.
- Двигаясь снизу вверх будем рассматривать подмножества.
- Если мы можем дописать в текущее подмножество минимальный элемент из удалённых, то мы нашли следующее разбиение и нужно завершить цикл.
- Если дописать не можем, значит, либо нужно укоротить и заменить какой-то элемент в текущем подмножестве, либо перейти к следующему подмножеству. Будем идти справа налево и рассматривать элементы:
- Если мы можем заменить текущий элемент минимальным удалённым — мы нашли следующее разбиение, завершаем оба цикла и выполняем алгоритм дальше. Стоит отметить, что нельзя перезаписывать последний элемент в подмножестве, иначе мы не сможем дописать минимальный хвост после этого подмножества — в удалённых будет элемент меньше текущего и мы не сможем выписать удаленные элементы так, чтобы получилось корректное разбиение.
- Если заменить текущий элемент каким-то из удалённых нельзя, то следует удалить и этот.
- Допишем лексикографически минимальный хвост подмножеств из оставшихся удалённых элементов.
list<list<int>> nextSetPartition(list<list<int>> a): used = list<int> // a — список, содержащий подмножества // used — список, в котором мы храним удаленные элементы fl = false for i = a.size - 1 downto 0 if (used.size != 0) and (max(used) > a[i][-1]) // в удалённых есть хотя бы один элемент, который мы можем добавить в конец. m = минимум из used строго больше a[i][-1] a[i].add(m) //добавляем used.remove(m) break for j = a[i].size - 1 downto 0 if (used.size != 0) and (j != 0) and (max(used) > a[i][j]) //если можем заменить элемент, другим элементом из списка used и он не последний m = минимум из used строго больше a[i][-1] a[i][j] = m //заменяем used.remove(m) fl = true break else used.add(a[i][-1]) a[i].pop() if a[i].size == 0 a.pop() if fl break //далее выведем все удалённые, которые не выбрали sort(used) for i = 0 to used.size - 1 a.add(list<int>(used[i])) //добавляем лексикографически минимальных хвост return a
Пример работы
Рассмотрим следующее разбиение:
1 | 2 | 3 |
4 | 5 |
1 Шаг:
1 | 2 | 3 | |
4 | 5 | ||
^ | Удалили элемент 5. | ||
Удалённые элементы |
2 Шаг:
1 | 2 | 3 | |
4 | |||
^ | Удалили элемент 4. Так как он является последним в подмножестве, то мы не можем заменить его на другой. | ||
5 | Удалённые элементы |
3 Шаг:
1 | 2 | 3 | 4 | |
^ | Дополнили первое подмножество элементом 4 | |||
5 | Удалённые элементы |
4 Шаг:
1 | 2 | 3 | 4 | |
5 | Дописали лексикографически минимальный хвост | |||
Удалённые элементы |