Блог пользователя Diplomate

Автор Diplomate, история, 8 лет назад, По-русски

Не раз вижу, что для использования какого-нибудь алгоритма, имеющего дело с большими числами, нужно предварительно сжать эти числа, однако поиск самого алгоритма сжатия ничего не дал. Правильно ли я понимаю, что для этого нужно создать массив пар <число, ссылка на число в прежнем массиве>, отсортировать его и по порядку перенумеровать числа с 1?

  • Проголосовать: нравится
  • +1
  • Проголосовать: не нравится

»
8 лет назад, # |
  Проголосовать: нравится +5 Проголосовать: не нравится
Как я делаю сжатие

В массиве B уже сжатый массив A.

  • »
    »
    8 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    И... оно фигово работает, когда в массиве a есть одинаковые числа.

  • »
    »
    8 лет назад, # ^ |
    Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

    Насколько я понимаю, нужно увеличивать присваиваемое b[a[i].second] число только тогда, когда очередное a[i] имеет значение, отличное от значения a[i-1]. Тогда этот алгоритм будет корректным.

    Да и таким способом не очень удобно восстанавливать первоначальные значения.

»
8 лет назад, # |
  Проголосовать: нравится +6 Проголосовать: не нравится

Короче, есть три способа.

  1. Посортить, как уже выше написали, пары (a[i], i). Потом в новый массив записывать числа по порядку на индексы a[i].second, при этом если a[i].first не меняется, записываемое число тоже не должно меняться.

  2. Добавить все числа в сет. Потом пройтись по сету и сохранить в мапе, что первый элемент сета будет 1, второй элемент сета будет 2, и т.д. Потом заменить числа в исходном массиве как a[i] = mp[a[i]].

  3. (мне кажется, это лучший способ) Посортить копию массива и убрать дубликаты. Потом бинпоиском найти позицию каждого элемента в этом массиве: a[i] = lower_bound(copy.begin(), copy.end(), a[i]) — copy.begin()

  • »
    »
    8 лет назад, # ^ |
    Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

    А что, если мне потом надо будет быстро преобразовывать сжатое число в несжатое? В новом массиве хранить пары <сжатое число, номер в несжатом и неотсортированном>?

    • »
      »
      »
      8 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      Обычно обратно преобразовывать не нужно (я ни разу не видел, чтоб было нужно), но если все-таки нужно, наиболее очевидный способ — просто сохранить эту инфу в map. Даже можно не в map, а просто в массив, т.к. все ключи — сжатые числа, и они от 1 до n.

      • »
        »
        »
        »
        8 лет назад, # ^ |
          Проголосовать: нравится +1 Проголосовать: не нравится

        Обратно бывает нужно во всяких задачах на отрезки (типа площади объединения прямоугольников). В твоём случае никакой map не нужен же, достаточно посмотреть на copy[i].

»
8 лет назад, # |
Rev. 2   Проголосовать: нравится +34 Проголосовать: не нравится
int a[maxn]; // исходный массив
int shr[maxn]; // сжатые координаты, shr -- от shrink
int k; // количество различных
for (int i = 0; i < n; ++i)
    shr[i] = a[i];
sort(shr, shr+n);
k = unique(shr, shr+n) - shr;

// индекс по исходному значению
int idx = lower_bound(shr, shr+k, val) - shr;

// исходное значение по индексу
int val = shr[idx];

Tips&tricks:

  1. Добавить в массив shr 0 и MAXN (или правую/левую границу прямоугольника, если исходные числа -- это координаты точек в прямоугольнике). Так будет удобнее писать штуки на полуинтервалах.
  2. Если нужен только относительный порядок, сразу после инициализации shr делаем
for (int i = 0; i < n; ++i)
    a[i] = lower_bound(shr, shr+k, a[i]) - shr;

и забываем про shr.

3. Не надо использовать map. Это в разы дольше, я так ловил TL-и. map стоит использовать только если координаты не известны заранее.

»
8 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

всегда пишу простой код с sort+lower_bound, но вообще можно обойтись одной сортировкой и одним дополнительным массивом:

Spoiler
»
8 лет назад, # |
Rev. 3   Проголосовать: нравится +15 Проголосовать: не нравится
void compress(vector<int>& A) {
    map<int, int> M;
    for (int x : A)
        M[x]; // не опечатка
    int idx = 0;
    for (auto& it : M)
        it.second = idx++;
    for (int& x : A)
        x = M[x];
}