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

Автор Sehnsucht, 9 лет назад, По-русски

Пытаюсь впихнуть невпихуемое задачу на битмаски, где можно брать либо 1 либо 2 предмета, с TL до 4000ms
Пока смотрю, сколько времени будет работать максимальный тест. Вот тут код:

#include <bits/stdc++.h>
using namespace std;

int main() {
    int p = 24;     // Кол-во предметов максимум
    int al = (1 << p);      // Кол-во масок, "1" - предмет под i-тым битом уже взят
    int vec[1 << 24];    // Ответы к подзадачам
    int vec2[1 << 24];
    int inf = 1000000005;
    fill(vec, vec + al, inf);
    vector<int> kek;
    vec[0] = 0;
    for(int z = 1; z < al; z++) {
        // Вместо перебора 24 * (24 - 1) / 2 значений перебираем
        // места, где есть биты
        kek.clear();
        for(int ss = 0; ss < 24; ss++)
            if(z & (1 << ss))
                kek.push_back(ss);

        // Оптимальный ответ
        int last = inf;

        for(int a = 0; a < kek.size(); a++) {
            int az = z - (1 << kek[a]);
            if(last > vec[az] + 1)
                last = vec[az] + 1;
            for(int b = a + 1; b < kek.size(); b++) {
                int bz = az - (1 << kek[b]);
                if(last > vec[bz] + 2)
                    last = vec[bz] + 2;
            }
        }

        //vec[z] = last;
    }
}

Предполагается, что в переменной last сохраняется оптимальный ответ (конечно, не просто vec[xx]+2, а по нужной формуле расстояний между предметами kek[a] и kek[b])
Запуск codeforces этого кода в GNU C++ работает за 1309ms (погрешность +-15ms)
Если раскомментировать строку "vec[z] = last;" это работает за 4040ms
Наконец, если завести новый массив vec2 и заменить строку на "vec2[z] = last;", это работает практически за то же время что в первый раз — от 1294ms до 1310ms

Вот это прикол! Какое его научное обоснование?

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

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

Ну що тут казатi

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

Возможно, или из-за кэша, или из-за оптимизатора.

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

Возможно, что попеременное обращение к vec[az/bz] и vec[z] очень хорошо убивает кеш процессора, и код постоянно обращается к сравнительно медленной оперативной памяти. Если предположение верное, то при постепенном уменьшении размера массива наступит момент, когда он целиком поместится в кеше, и эффект внезапно пропадёт.

Если vec2 нигде после присваивания не используется, то оптимизатор его уберёт, и будет то же, что и в первом случае.

Вообще для чистоты эксперимента программе следует считывать данные и выводить результат. Потому что иначе компилятор может подумать "ой, да эта программа же ничего не делает, давай-ка соптимизируем её в return 0;".

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

    Ага, после кода "cout << vec2[0];" все стало на свои место — больше 4000ms :)

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

    Последний абзац — я так по первости один раз попался. :))) Меняю на release — моментально считает 1,000,000 primes. Ставлю debug — вообще не дождаться. Я долго глючил, пока понял, что оптимизатор просто на фиг выбросил мои расчёты, потому что ничего не выводилось на экран и никуда не записывалось :))

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

Попробуйте локально у себя запустить с различными параметрами оптимизации.

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

Кажется условие ниже никогда не выполнится.

      int last = inf;
...
            if(last < vec[az])

Учитывая, что компилятор знает все значения в vec (0 и куча inf) и то что он не меняется, я бы на его месте выпилил этот цикл совсем.

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

    Изменил, все как прежде

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

      Как уже писали выше: если вы переменную заполняете, но не используете — компилятор может выпилить её. Кажется если вы добавите cout << vec2[al - 1] << endl; в конце, то получите те же "тормоза", что и в случае с vec.