[Все темы по размещениям]
Теория: Окулов. Программирование в алгоритмах. 2004
Практика: K4.Размещения.(dl.gsu.by)
Пусть n = 9, а m = 6. Рассмотрим текущее размещение Pi = {1,3,6,5,9,8}. Найдем следующее размещение Pi+1 в лексикографическом порядке. Алфавитом в данном случае будет являться набор цифр от 1 до 9.
Для начала разделим все элементы алфавита на две группы: те, что входят в текущее размещение Pi и те, что нет(множество F - свободные члены):
136598247
Будем последовательно перебирать элементы размещения Pi справа налево. Если на каком-то этапе для текущего элемента размещения Pi[j] можно найти элемент F[k] из свободных членов, который больше его, то заменяем Pi[j] на F[k]. При этом F[k] должен быть минимально возможным. При этом может возникнуть две ситуации:
1) Это сделать удалось. При это нужно дополнить размещение так, чтобы ее размер стал равным m. Для этого на свободные места последовательно добавляем элементы множества F в порядке возрастания начиная с самого маленького.
2) Этого сделать не удалось. Текущий элемент Pi[j] удаляем из текущего размещения и добавляем в множество F. Берем следующий элемент Pi[j-1] и повторяем итерацию. Если же не удалось найти такой элемент, значит на текущей итерации мы работали с последним размещением в лексикографическом порядке.
Давайте получим размещение Pi+1 по вышеизложенным правилам:
Pi = | 136598247 | Нет свободного элемента больше 8 |
136592478 | Нет свободного элемента больше 9 | |
136524789 | Есть 3 свободных элемента больше 5: 7,8,9. Выбираем 7 и меняем их местами | |
136724589 | Первые 4 элемент размещения Pi+1 определены | |
Pi+1 = | 136724589 | Дополняем префикс размещения Pi+1 |
Данный подход можно реализовать со сложностью O(N*N):
- string letters = "1234567";
- bool next_placement(string &cur, vector<bool> &usd, int n, int m) {
- int beg = -1;
- for (int i=m-1;i>=0;--i) {
- if (beg != -1) break;
- for (int j=cur[i] - letters[0] + 1; j < n; ++j) {
- if (!usd[j]) {
- usd[j] = true;
- usd[cur[i] - letters[0]] = false;
- cur[i] = letters[j];
- beg = i;
- break;
- }
- }
- if (beg == -1)
- usd[cur[i] - letters[0]] = false;
- }
- if (beg != -1) {
- for (int i=beg+1, j = 0; i<m && j<n;++j) {
- if (!usd[j]) {
- usd[j] = true;
- cur[i++] = letters[j];
- }
- }
- return true;
- }
- else
- return false;
- }
* This source code was highlighted with Source Code Highlighter.
Рассмотрим параметры, передаваемые в функцию next_placement:
1. cur – текущее размещение.
2. usd – массив меток. Если usd[i] == false, значит i-ый элемент алфавита является свободным членом.
Т.к. текущее размещение cur передается по ссылке, то после выполнения функции оно заменится на следующее размещение в лексикографическом порядке.
Полная реализация: здесь
Немного подумав, можно смело заменить массив меток на множество(set) свободных членов, улучшив асимптотику до O(N*log(N)):
- bool next_placement(string &cur, set<char> &vac, int n, int m) {
- set<char>::iterator it;
- for (int i=cur.size()-1;i>=0;--i) {
- it = vac.lower_bound(cur[i]);
- if (it != vac.end()) {
- char tmp = cur[i];
- cur[i] = *it;
- vac.erase(it);
- vac.insert(tmp);
- for (int pos = i+1; pos < m; ++pos) {
- cur[pos] = *vac.begin();
- vac.erase(vac.begin());
- }
- return true;
- }
- vac.insert(cur[i]);
- }
- return false;
- }
* This source code was highlighted with Source Code Highlighter.
Полная реализация: здесь
Комментариев нет:
Отправить комментарий