Имея отсортированную гистограмму из n баров, выберите k баров, минимизируя площадь, ограниченную правой стеной.

Даны целое число k и отсортированная гистограмма с n столбцами высотой a1, a2, a3,..., an (эта последовательность не убывает, так как гистограмма отсортирована). Из этих n баров нужно выбрать k бара(1 <= k <= n) так, чтобы площадь, заключенная между выбранными барами и правой стенкой гистограммы, была минимально возможной.

Например, для последовательности высот {1, 3, 5, 9}, если мы выбрали стержни с высотами 1, 5; площадь, окруженная правой стеной, будет 12 единиц и будет выглядеть примерно так, как на изображении (предположим, что ширина полос равна 1 единице).

Пример гистограммы

Попробовав несколько жадных подходов (которые были неправильными), я перешел к следующему решению динамического программирования:

Определим dp[i][j][last] как минимальную площадь при выборе j баров из первых i баров гистограммы таким образом, чтобы предыдущий бар, который мы взяли справа, имел индекс last.

dp[i][j][last] = min(dp[i - 1][j][last], dp[i - 1][j - 1][i] + a[i] * (last - i));

Но проблема в том, что он слишком медленный, его O(n2k), поэтому я надеюсь, что кто-нибудь поможет мне оптимизировать его до чего-то вроде O( nk) или, может быть, предложите более быстрое жадное решение.


person Manish Chandra Joshi    schedule 07.07.2019    source источник
comment
Re: это O(n³): если я правильно понимаю ваш подход, на самом деле это O(n²k).   -  person ruakh    schedule 07.07.2019
comment
Должно быть, я неправильно понимаю. Скажем, k = 2. Если я выбираю бары n и n-1, то площадь, заключенная между выбранными барами и правой стенкой гистограммы, равна 0 и, следовательно, минимальна. То же самое с k = 3, если я выбираю такты n, n-1 и n-2.   -  person RobertBaron    schedule 07.07.2019
comment
@RobertBaron Это похоже на область в тени, если мы направляем свет слева направо, но также и сами полосы. Обратите внимание, что полосы имеют ширину 1 единицу.   -  person Manish Chandra Joshi    schedule 08.07.2019
comment
Можем ли мы предположить, что высоты столбцов должны принадлежать натуральным числам?   -  person Ninetails    schedule 11.07.2019


Ответы (2)


В дальнейшем я буду предполагать, что k >= 2, поскольку в противном случае проблема не решается тривиально.

Вот частичное решение в том смысле, что оно может решить часть проблемы за O(n) времени, если форма стержней имеет некоторые специфические свойства. В частности, мы можем сделать следующие выводы:

Если объем, покрываемый всеми барами, выпуклый, то оптимальное решение содержит только бары в начале и в конце.

Доказательство:

Предположим, что в решении есть какое-то разделение, тогда мы можем переместить группу, которая находится от одного из ребер, к одному из ребер. Это делается путем отмены выбора полосы с одной стороны и выбора новой с другой стороны. Так как форма выпуклая, кривизна должна быть неположительной, то при движении в невозрастающем направлении уменьшение при дальнейшем движении в этом направлении должно быть не меньше (кривизна это обеспечивает). Таким образом, мы можем переместить разбиение решения на одно из ребер без увеличения (и, вероятно, уменьшения) покрываемой площади.

Мы можем проверить за O(n) время, является ли фигура выпуклой (невозрастающая разница между полосами), и мы можем решить проблему с оптимизацией скользящего окна, что просто сделать в O(n). Таким образом, мы можем предварительно обработать любое другое решение, чтобы уменьшить набор задач до задачи, содержащей хотя бы одну вогнутую область. Если мы сможем найти подзадачи для других алгоритмов, которые также содержат это свойство, то мы также сможем решить их отдельно в этом смысле.

Для вогнутых областей они могут иметь стабильную внутреннюю область (куда могут притягиваться новые расщепления) в дополнение к внешним возможным стабильным областям (куда могут притягиваться другие расщепления, потому что, даже если разница в площади при таком перемещении положительный, движение все еще может быть отрицательным). Используя это, мы можем описать полностью вогнутую область участками, где расколы будут двигаться к краям или стабильной средней точке.

Обратите внимание, что многое из вышеперечисленного падает, когда вогнутые и выпуклые области соединены, поскольку критерии стабильности зависят от наличия границы в начале и конце, от которых мы измеряем. Хотя можно было бы решить всю проблему, опираясь на этот подход, я не уверен, насколько он поможет на произвольно сложных формах.

Требование к вогнутой области иметь внутреннюю стабильную область, как правило, довольно жесткое (в глобальном масштабе), и я не уверен, что вы вообще можете получить очень много из них в глобальном масштабе, поэтому вот алгоритм, который используйте это, чтобы решить проблему в O(n), O(kn) или O(kn^2), в зависимости от сложности проблемы, просматривая различные эвристики и переходя к более дорогой, если мы не уверены, что нашли оптимальное решение.

Сначала мы вычисляем базовый результат в режиме скользящего окна (O(n)) и сохраняем этот результат. Затем мы ищем области стабильности для одной точки (эти внешние области уже рассматриваются в скользящем окне), которая указывает в сторону от краев и имеет площадь, меньшую, чем найденное решение. Если таких точек более 1 (требуется специальная форма), по умолчанию возвращается базовый алгоритм, такой как у Маниша Чандры Джоши. Если найдена одна такая точка, мы переходим к решению O(nk), если нет, принимается текущее решение. Обратите внимание, что мы могли бы расширить приведенную ниже версию до более чем 1 такой точки глобальной стабильности, но на практике потребуется, чтобы они были достаточно близки, поскольку в противном случае они, как правило, позже обнаруживаются как сбои.

В решении «O(kn)» мы решаем скользящее окно отдельно по обе стороны от глобальной точки устойчивости посередине для каждого из kвозможных количеств назначений баров по обеим сторонам и выбираем лучшее из этих решений. . Затем мы снова ищем области стабильности внутри каждой из областей (помните, что это означает, что раскол не будет двигаться к ребру) и проверяем, соответствует ли площадь оптимальной из этих точек вместе с центральной точкой, которую мы вычисляем. for имеет меньшую площадь, чем меньшее из решений «O (kn)» и «O (n)». Если такая точка найдена, мы должны принять полное решение (как у Маниша Чандры Джоши), в противном случае мы можем принять лучшее из двух вычисленных нами решений.

Обратите внимание, что должна быть возможность включить большую граничную область как безопасную или легко выводимую из безопасного решения в приведенном выше алгоритме и, таким образом, увеличить количество случаев, когда мы не возвращаемся к более медленному алгоритму. В частности, область, удаленная примерно на «k» от края, может иметь простые решения или на практике уже быть покрыта более ранними решениями. Обратите внимание, что вычисляя скользящие окна как окна непокрытой области, мы должны иметь возможность генерировать некоторые простые решения для таких случаев в сочетании с данными из решений динамического программирования в случае «O (kn)».

person Ninetails    schedule 07.07.2019
comment
Вероятно, пройдет какое-то время, прежде чем у меня будет время действительно сесть и написать полный код для этого, и я не планировал делать это изначально, поскольку в вопросе не было тега языка. Я могу понять, что особенно последний алгоритм может быть слишком сложным в своем объяснении, чтобы стоять самостоятельно. - person Ninetails; 08.07.2019
comment
@גלעדברקן У меня есть решение, и я написал ответ. - person Manish Chandra Joshi; 08.07.2019

Частичное решение Ninetails покрывает некоторую часть ответа, и я выяснил несколько других случаев, которыми я поделюсь.

У меня нет доказательств этого, но оптимальные бары, которые мы берем, всегда формируют ряды форм 1010, или 10, или 01, или 010, или 101, или тривиального 1. (Здесь 1 означает серию баров, которые мы взяли, а 0 означает серию баров, которые мы не взяли)

Это означает, что оптимальные стержни всегда находятся либо в одной непрерывной группе, либо в двух смежных группах, причем одна группа касается самого левого края. Я проверил этот факт, используя генератор случайных тестов с перебором O(2nn2) и O(n2k ) решение для динамического программирования, запустив его для нескольких тысяч случаев.

Таким образом, с этим пониманием мы можем легко найти ответ за O (n * k), используя массив сумм префиксов для эффективного поиска сумм диапазонов. Вот окончательный код на С++ (с небольшими комментариями)

using ll = long long;
    ll n, z;
    cin >> n >> z;
    vector<ll> a(n);
    for (auto &e : a) {
        cin >> e;
    }
    assert(is_sorted(a.begin(), a.end()));

    ll stratAns, ans = INF;

    // prefix sum array
    vector<ll> pref(n + 1);
    for (int i = 1; i <= n; ++i) {
        pref[i] = pref[i - 1] + a[i - 1];
    }

    stratAns = INF;

    /// strategy 1 : handles cases where runs like 10, 01, 010, 1 are optimal to choose

    for (int starting = 0; starting + z - 1 < n; ++starting) {
        int ending = starting + z - 1;
        ll curAns = 0;

        //~ for (int i = starting; i <= ending; ++i) {
            //~ curAns += a[i];
        //~ }
        // doing the same with prefix sums instead
        curAns += pref[ending + 1] - pref[starting];

        curAns += a[ending] * (n - ending - 1);
        stratAns = min(stratAns, curAns);
    }
    ans = min(ans, stratAns);

    stratAns = INF;

    /// strategy 2 : handle cases when runs 1010 and 101 are optimal

    for (int last = z - 1; last < n; ++last) {
        for (int x = 1, y = z - 1; y > 0 && x < z; x++, y--) {
            assert(x + y == z);
            ll curAns = 0;

            //~ for (int i = 0; i < x; ++i) {
                //~ curAns += a[i];
            //~ }
            // performing the same with prefix sums instead
            curAns += pref[x];

            curAns += a[x - 1] * (last - y + 1 - x);

            //~ for (int i = last - y + 1; i <= last; ++i) {
                //~ curAns += a[i];
            //~ }
            // performing the same with prefix sums instead
            curAns += pref[last + 1] - pref[last - y + 1];


            curAns += a[last] * (n - last - 1);
            stratAns = min(curAns, stratAns);
        }
    }
    ans = min(ans, stratAns);
    cout << ans << endl;
person Manish Chandra Joshi    schedule 08.07.2019
comment
Должна быть возможность иметь более сложные последовательности, но их может быть довольно сложно найти в случайных данных. Последовательность [21,22,24,25,2000,3000] должна иметь вид 01010, если нужно выбрать 2 такта. Мне действительно пришлось написать набор формул для условий, чтобы его сгенерировать: (y+2)b < a-1 и (1-y)a < b-1, где первая цель имеет высоту a, вторая a+b и после второй цели y+1 баров. Наличие более чем одной полосы между целями только усложняет задачу. Как видите, их довольно сложно найти с помощью случайно сгенерированных тестов. - person Ninetails; 09.07.2019
comment
Оптимальная площадь для случая, который вы привели, составляет 99 единиц, и единственный способ получить ее — выбрать столбцы высотой 24 и 25 единиц. Таким образом, они имеют форму 001100 или в сжатом виде просто 010 (один непрерывный блок). Можете ли вы получить еще лучшую область? РЕДАКТИРОВАТЬ: я использовал исчерпывающую 2 ^ n грубую силу, чтобы найти этот оптимальный ответ. - person Manish Chandra Joshi; 09.07.2019
comment
(Вы можете упомянуть @Ninetails, если хотите, чтобы они были уведомлены.) - person גלעד ברקן; 10.07.2019
comment
Попробую еще раз пройтись по формулам: должно быть 3 соответствующих варианта (с y = 0), 11000, 00110 и 01010. Пусть первая высота будет a, вторая будет a+b, дополнительное число между двумя шагами xи дополнительное число после 2-го шага y. Мы требуем, чтобы стоимость 01010 в некоторой конфигурации была доступна для выбора. Стоимость 01010 составляет 4a+2b +(ax+(a+b)y). Стоимость 11000 составляет 5a-1+(ax+ay), а стоимость 00110 составляет 3a+3b-1+(ay+by). Это дает нам неравенства: 4a+2b +(ax+(a+b)y)<5a-1+(ax+ay) и 4a+2b +(ax+(a+b)y)<3a+3b-1+(ay+by). Продолжение в следующем комментарии... - person Ninetails; 10.07.2019
comment
Теперь упростим неравенства: 4a+2b +(ax+(a+b)y)<5a-1+(ax+ay) => 2b +by<a-1 и 4a+2b +(ax+(a+b)y)<3a+3b-1+(ay+by) => a +ax< b-1. Здесь я вижу, что допустил ошибку в частях x и y ранее, и любая установка x>0 и y>0 только усугубляет ситуацию, поэтому мы устанавливаем их в 0 и получаем простой набор неравенств: 2b < a-1 и a > b-1, который, очевидно, не может быть удовлетворены, так что мы только что доказали, что 01010 никогда не может произойти, что также включает в себя все производные, такие как 101010, 0101010 и так далее. С этими знаниями мне интересно, сможем ли мы найти O(n) решение. - person Ninetails; 10.07.2019
comment
(@גלעדברקן) Последовательность должна быть отсортирована, так что это будет [1, 49, 70, 102, 141], для этой последовательности стоимость 10101 равна 283, а оптимальной является 10011 со стоимостью 246. Хотя доказательство, представленное выше, не запрещает 10101 структур (оно запрещает 2 различные нереберные группировки), я могу представить, что его можно было бы расширить, чтобы также запретить структуру 10101, для которой у нас есть только постулат о ее запрещении (эмпирические данные, как правило, предполагают только постулаты в математике), поэтому доказательство этого является открытый вопрос. - person Ninetails; 11.07.2019
comment
@Ninetails О, ой, не подумал о сортировке. Спасибо! - person גלעד ברקן; 11.07.2019