Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Волосатый бизнес (разомнёмся и разбогатеем), задачка для ума

713
11 сентября 2006 года
Ap0k
360 / / 13.03.2006
Приветсвую всех.
Вот сейчас прислали ссылку на очень интересный сайт, посвященный спортивному программированию. Зашёл в раздел тренинга и увидел интересную задчку Волосатый бизнес . Решил немного себя помучить, да и давно уже не приходилось сталкиваться с подобными вещами, разве что в школе на олимпиаде :-)
Предлагаю присоединиться и найти таки идеальное решение данной задачи :-)
Для ленивых (взято с ttb.by):
Цитата:

[SIZE="3"]Волосатый бизнес[/SIZE]
[COLOR="Silver"]автор IvanMetelsky aka Иван Метельский[/COLOR]
Ограничение на время: 1.0 сек
Ограничение на память: 64 МБ
Ограничение на размер кода: 16536 байт


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

Известно, что пункты приема покупают волосы произвольной длины. Стоимость волос прямо пропорциональна их длине, то есть она может быть вычислена как C*L, где C - это цена одного сантиметра волос, а L - это длина волос в сантиметрах. Так как волосяной рынок является очень динамичным, то цена одного сантиметра волос не является постоянной и очень часто меняется.

Неформал является очень хорошим бизнес-аналитиком. Он смог вычислить, какой будет цена одного сантиметра волос в каждый из ближайших N дней (для удобства пронумеруем дни в хронологическом порядке от 0 до N-1). Теперь он хочет определить, в какие из этих дней ему следует продавать волосы, чтобы по истечению всех N дней заработать максимальное количество денег.

Для упрощения задачи сделаем следующие предположения.

Волосы у неформала растут только ночью и за одну ночь вырастают ровно на 1 сантиметр.
За день до 0-го дня неформал с горя постригся налысо, но сдать волосы в пункт приема не догадался. За прошедшую ночь волосы выросли на 1 сантиметр, и поэтому в 0-й день их длина равняется одному сантиметру.
Сдавать волосы можно только днем. При сдаче волос неформала стригут налысо (т.е. длина его волос становится равной 0 сантиметрам) и оплачивают стоимость состриженных волос согласно цене одного сантиметра в день сдачи.

Входные данные.

Массив целых чисел cost содержит ровно N элементов и описывает цены на волосы в каждый из ближайших N дней. Элемент cost равен цене одного сантиметра волос в i-й день. Цена выражена в некоторых условных денежных единицах (у.е.).

Выходные данные.

Целое число, равное максимальной денежной сумме, которую неформал может заработать на продаже своих волос по истечению ближайших N дней. Сумма должна быть выражена в у.е.

Описание:

Класс: HairyBusiness
Метод: getMaximumProfit
Параметры: vector<[COLOR="#0000ff"]int[/COLOR]>
Возвращаемое значение: [COLOR="#0000ff"]int[/COLOR]
Сигнатура метода: [COLOR="Blue"]int[/COLOR] getMaximumProfit(vector<[COLOR="#0000ff"]int[/COLOR]> cost)

Ограничения:

Число N от 1 до 100 включительно.
Количество элементов в массиве cost равно N.
Каждый элемент массива cost от 1 до 100 включительно.
Количество тестов не превосходит 50.

Примеры:
  • cost = {73, 31, 96, 24, 46}
    result = 380


Неформалу следует сдать волосы во 2-й и в 4-й дни. Во 2-й день длина его волос будет равна 3 сантиметра и за них он получит 3*96 = 288 у.е. В 4-й день длина волос будет равна 2 сантиметра и за них заплатят 2*46 = 92 у.е. Суммарная прибыль будет равна 288+92 = 380 у.е.
  • cost = {1}
    result = 1
  • cost = {100, 100, 100, 100, 100}
    result = 500
  • cost = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    result = 100
  • cost = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}
    result = 55



Удачи ;-)

17K
12 сентября 2006 года
DIME
24 / / 07.09.2006
int max=cost[0];
int out;
int inp=0;
int sum=0;
bool d=false;
do
{
for (int i=inp;i<N;i++)
{
if(max<cost)
{
max=cost;
out=i;
}
}
sum+=(out-inp)*cost[out];
max=0;
if (out!=N-1)
inp=out+1;
else
d=true;
}while(d)
return sum;

ну гдето так, нет возможности проверить
350
15 сентября 2006 года
cheburator
589 / / 01.06.2006
Кажись, задача скорее математическая, чем програмерская, и наверняка решение лежит в области оптимизационной задачи линейного программирования. Кажись.
350
16 сентября 2006 года
cheburator
589 / / 01.06.2006
Да, действительно, математика очень помогла. Хотя обошлось без линейного программирования - всё оказалось гораздо проще.

Есть "тупое", прямое решение - перебор всех вариантов. Вариантов, очевидно, 2^N, где N - количество дней. Тогда сложность алгоритма составит соответственно O (2^N), т. е. при увеличении кол-ва дней на 1 программа будет работать вдвое дольше.
Мой пень-4 2,4 ГГц вычислял 0,3 сек. для 20 дней, 5 минут для 30 дней, а 100 дней будет вычислять несколько триллионов лет.

Однако займемся математикой:
1. Очевидно, в последний день в любом случае надо стричься. Т. е. последняя стрижка будет в последний день.

2. Предположим, мы нашли, что в некоторый j-й день выгодно будет постричься. Посмотрим, выгодно ли стричься в i-й день (i < j). Возникает два варианта:

2а. До i-го дня стрижек нет. Тогда длина волос в i-й день составит i+1, цена стрижки соответственно (i+1) * Ci. К j-му дню волосы вырастут длиной j-i. Соответственно, прибыль, которую мы получим за две стрижки (i-й день и j-й день) составит
s1 = (i+1) * Ci + (j-i) * Cj
А если мы не будем стричься в i-й день, прибыль составит
s0 = (j+1) * Cj
Разность между этими величинами
s1 - s0 = i * Ci + Ci + j * Cj - i * Cj - j * Cj - Cj = i*Ci + Ci - i*Cj - Cj = (i+1) * (Ci - Cj)
Нас интересует только знак этой разницы - если она положительна, стричься в i-й день выгодно, если нет - не выгодно. Знак разницы зависит только от знака величины Ci - Cj, т. е. в i-й день стричься выгодно тогда и только тогда, когда Ci > Cj.

2b. До i-го дня есть стрижка, которая происходит в k-й день (т. е. k < i). Тогда суммарная прибыль, зарабатываемая в i-й и j-й дни, составит, если стричься в i-й день:
s1 = (i-k) * Ci + (j-i) * Cj
А если не стричься:
s0 = (j-k) * Cj
Опять же, оценим разность:
s1 - s0 = i*Ci - k*Ci + j*Cj - i*Cj - j*Cj + k*Cj = i * (Ci - Cj) - k (Ci - Cj) = (i-k) * (Ci - Cj)
Снова, поскольку k < i, знак этой разницы зависит только от Ci - Cj, т. е. опять же стричься в i-й день выгодно тогда и только тогда, когда Ci > Cj.

Как видим, в любом случае единственным критерием оценки выгодности стрижки в i - й день является её стоимость.

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

Сложность такого алгоритма составляет O (N). Вообще говоря, дни стрижки таким способом нетрудно определить и без компьютера.

Вот примерный вариант кода. Предполагается, что число N и массив цен уже введены пользователем в некоторые переменные. В третьем параметре функция вернет признаки стрижки для каждого дня (true - в этот день стрижемся, false - не стрижемся; массиву должно быть предварительно выделено место под N элементов). Массив цен не должен содержать отрицательных значений:
Код:
int getMaximumProfit (const int N, const int *cost, bool *days)
{
  int LastCost = -1;

  for (int i=N-1; i>=0; i--)
  {
    if (LastCost < cost)
    {
      days = true;
      LastCost = cost;
    }
    else
      days = false;
  }

  int profit = 0;
  int LastDay = -1;
  for (int i=0; i<N; i++)
  {
    if (days)
    {
      profit += (days - LastDay) * cost ;
      LastDay = days;
    }
  }

  return profit;
}
3.0K
16 сентября 2006 года
Мerlin
267 / / 25.07.2006
cheburator, ты как проффессионал, который пишет

return ((int*)arg2)[1] - ((int*)arg1)[1];

можешь объяснить, мне, непроффессионалу, который пишет

return *(int *)((int)arg2+4) - *(int *)((int)arg1+4);

смысл кода:
 
Код:
if (days)
    {
      profit += (days - LastDay) * cost ;
      LastDay = days;
    }

Ведь days
  • это bool, т.е. он равен или 1, или 0. Внутри if-блока он всегда равен 1,
    а LastDay первый раз равен -1, в остальных случаях равен 1.
    Т.е. profit = 2*cost, где i это первый день, в которой неформал решил постричся,
    а остальные дни НЕ УЧИТЫВАЮТСЯ. Правильнй код:
    Код:
    int profit = 0;
      int LastDay = 0;
      for (i=0; i<N; i++)
      {
        LastDay++;
        if (days)
        {
          profit +=  LastDay * cost ;
          LastDay = 0;
        }
      }
    и прога, только с одним циклом
    Код:
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <iostream.h>
    #include <conio.h>

    const int N = 10;
    int cost[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
    bool days[N];

    int getMaximumProfit (const int n, const int *cost, bool *days)
    {
      if(n==1)
      {
        *days = true;
        return *cost;
      }
      int LastCost  = cost[n-1];
      int DaysCnt = 1;
      int DayNdx  = n - 1;
      int profit  = 0;
      for (int i=n-2; i>=0; i--)
      {
        if (LastCost < cost)
        {
          profit += DaysCnt*LastCost;
          days[DayNdx] = true;
          DayNdx = i;
          LastCost = cost;
          DaysCnt = 1;
        }
        else
        {
          days = false;
          DaysCnt++;
        }
      }
      profit += DaysCnt*LastCost;
      days[DayNdx] = true;

      return profit;
    }

    void main()
    {
      int h = getMaximumProfit (N, cost, days);
      cout << h << endl;
      for(int i=0;i<N;i++)
      cout << days << endl;
      getch();
    }
  • 350
    18 сентября 2006 года
    cheburator
    589 / / 01.06.2006
    [QUOTE=Мerlin]смысл кода:
     
    Код:
    if (days)
        {
          profit += (days - LastDay) * cost ;
          LastDay = days;
        }

    Ведь days
  • это bool, т.е. он равен или 1, или 0. Внутри if-блока он всегда равен 1,
    а LastDay первый раз равен -1, в остальных случаях равен 1.
    Т.е. profit = 2*cost, где i это первый день, в которой неформал решил постричся,
    а остальные дни НЕ УЧИТЫВАЮТСЯ.[/QUOTE]
    Пардон, ошибся, я хотел написать
     
    Код:
    profit += (i - LastDay) * cost ;
          LastDay = i;

    (т. е. LastDay - день предыдущей стрижки). А насчет твоего варианта, да, программа с одним циклом менее громоздка, чем с двумя. Мне важно было показать основу алгоритма.
  • 3.0K
    18 сентября 2006 года
    Мerlin
    267 / / 25.07.2006
    [QUOTE=cheburator]Пардон, ошибся, я хотел написать
     
    Код:
    profit += (i - LastDay) * cost ;
          LastDay = i;

    (т. е. LastDay - день предыдущей стрижки).[/QUOTE]Если LastDay инициализировать 0-м, вместо -1, тогда покатить.
    350
    18 сентября 2006 года
    cheburator
    589 / / 01.06.2006
    [QUOTE=Мerlin]Если LastDay инициализировать 0-м, вместо -1, тогда покатить.[/QUOTE]
    Обрати внимание на условия задачи. В начале 0-го дня у чувака уже есть сантиметр волос! Поэтому в случае, когда i-й день - самая первая стрижка, длина волос равна i-(-1) = i+1. Поэтому в самом начале LastDay инициализируется минус единицей. Одновременно это и признак того, что стрижек ещё не было.
    И ещё. В твоей проге почему-то отдельно выделен случай одного-единственного дня (if (n==1)...). Зачем?
    3.0K
    18 сентября 2006 года
    Мerlin
    267 / / 25.07.2006
    [QUOTE=cheburator]Обрати внимание на условия задачи. В начале 0-го дня у чувака уже есть сантиметр волос! Поэтому в случае, когда i-й день - самая первая стрижка, длина волос равна i-(-1) = i+1. Поэтому в самом начале LastDay инициализируется минус единицей. Одновременно это и признак того, что стрижек ещё не было.[/QUOTE]Storno. Я не учел, что нумерация дней начинается с 0, а не с 1-ы.
    Цитата:
    И ещё. В твоей проге почему-то отдельно выделен случай одного-единственного дня (if (n==1)...). Зачем?

    Это дело вкуса. Можно и без того блока, но если вырожденный случай, imho, можно ограничиться 2-я командами.

    Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
    Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог