Вариация ранца на соревнованиях по программированию

Вопрос:

Экзамен состоит из N вопросов. Оценки N вопросов: m1, m2, m3, .. mN соответственно. Джем сдает экзамен и хочет получить максимальное количество оценок. Однако ему требуется некоторое время, чтобы решить каждый вопрос. Время, затрачиваемое им на решение вопросов, равно t1, t2, t3, .. tN соответственно. Экзамены длятся в общей сложности T. Но учительница Джем очень умна и знает, что Джем найдет способ получить максимальные оценки. Итак, чтобы сбить Джема с толку, она также предлагает ему бонусное предложение. Предложение состоит в том, что Джем может выбрать вопрос, за который он может удвоить оценки, полученные за этот вопрос. Теперь Джем действительно сбит с толку. Помогите ему узнать максимальное количество баллов, которое он может набрать.

Ограничения

1<=N<=1000

1<=T<=10000

1<=mi<=100000

1<=ti<=10000

Я попробовал этот вопрос здесь и придумал следующий алгоритм:

Поскольку задача гласит, мы можем выбрать вопрос, за который он может удвоить баллы, полученные за этот вопрос.

Итак, для выбора этого вопроса я применил жадный подход, т.е.

  1. следует выбрать вопрос с большим соотношением (баллы/время), за который он может удвоить баллы, присуждаемые за этот вопрос.
  2. И если это соотношение тоже такое же, то следует выбирать вопрос с большей оценкой.

    А в остальном просто ранец насколько я понял вопрос. Я попробовал следующую реализацию, но получил неправильный ответ. для данного тестового примера мой код дает правильный вывод

    3 10 1 2 3 4 3 4

я пробовал этот тестовый пример, указанный в разделе комментариев, и мой код дает 16 в качестве вывода, но ответ должен быть 18

  3 
  9
  9 6 5
  8 5 3

Подход грубой силы приведет к превышению ограничения по времени, поскольку решение N рюкзаков сложности O (nW) даст общую временную сложность O (n ^ 2 W). Я думаю, что для этого вопроса может быть разработан более конкретный алгоритм. У кого-нибудь есть идея получше, чем эта?

Спасибо

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int T[2][10002];
    int a[1000002],b[100002];
    float c[100002];
    int knapSack(int W,int val[],int wgt[],int n)
    {
    int i,j;

    for(i=0;i<n+1;i++)
    {
        if(i%2==0)
        {
            for(j=0;j<W+1;j++)
            {
            if(i==0 || j==0)
            T[0][j]=0;
            else if(wgt[i-1]<=j)
            T[0][j]=max(val[i-1]+T[1][j-wgt[i-1]],T[1][j]);
            else
            T[0][j]=T[1][j];
            }
        }
        else
        {
            for(j=0;j<W+1;j++)
            {
            if(i==0 || j==0)
            T[1][j]=0;
            else if(wgt[i-1]<=j)
            T[1][j]=max(val[i-1]+T[0][j-wgt[i-1]],T[0][j]);
            else
            T[1][j]=T[0][j];
            }
        }
    }
    if(n%2!=0)
        return T[1][W];
    else
        return T[0][W];
    }


    int main()
    {
    int n,i,j,index,t,mintime,maxval;
    float maxr;
    cin>>n;
    cin>>t;
    for(i=0;i<n;i++)
        cin>>a[i];

    for(i=0;i<n;i++)
        cin>>b[i];

    maxr=0;
    index=0;
    mintime=b[0];
    maxval=a[0];

    for(i=0;i<n;i++)
        {
            c[i]=(float)a[i]/b[i];  
                if(c[i]==maxr && b[i]<=t)
                {
                    if(a[i]>=maxval)
                    {
                    maxval=a[i];
                    mintime=b[i];
                    index=i;
                    }
                }   
                else if(c[i]>maxr && b[i]<=t)
                {
                maxr=c[i];
                maxval=a[i];
                mintime=b[i];
                index=i;
                }

        }

    a[index]=a[index]*2;
    int xx=knapSack(t,a,b,n);
    printf("%d\n",xx);
    }

person prashantitis    schedule 18.05.2014    source источник
comment
Почему вы используете жадное приближение, когда вопрос требует точного ответа?   -  person user2357112 supports Monica    schedule 18.05.2014
comment
@ user2357112: потому что я не мог придумать лучшего подхода   -  person prashantitis    schedule 18.05.2014
comment
Подход грубой силы заключается в том, чтобы решить N рюкзаков (в каждом из них выбирается еще один вопрос для удвоения) и взять общее лучшее решение.   -  person Henry    schedule 18.05.2014
comment
@ Генри: спасибо за предложение грубой силы, но это наверняка приведет к превышению срока   -  person prashantitis    schedule 18.05.2014
comment
@ user2357112: Я уже знаю грубую силу. Есть ли что-то лучше этого?   -  person prashantitis    schedule 18.05.2014


Ответы (2)


Чтобы решить эту проблему, давайте сначала посмотрим на статью в Википедии о задаче о рюкзаке, которая описывает динамическое программирование. решение штатной задачи:

// Input:
// Values (stored in array v)
// Weights (stored in array w)
// Number of distinct items (n)
// Knapsack capacity (W)
for j from 0 to W do
  m[0, j] := 0
end for 
for i from 1 to n do
  for j from 0 to W do
    if w[i] <= j then
      m[i, j] := max(m[i-1, j], m[i-1, j-w[i]] + v[i])
    else
      m[i, j] := m[i-1, j]
    end if
  end for
end for

(И, как говорится в статье, вы можете уменьшить использование памяти, используя одномерный массив m, а не двумерный массив).

Теперь мы можем использовать эту идею для решения расширенной задачи. Вы можете вычислить две таблицы: вместо m вы можете вычислить m0 и m1. m0[i, w] хранит максимальное значение, полученное с использованием первых i элементов с весом (в вашем случае время) не более w, без использования вопроса с двойной оценкой. Точно так же m1 хранит максимальное значение, полученное с использованием первых i элементов с весом (в вашем случае время) не более w и используя вопрос с двойной оценкой.

Правила обновления меняются на:

if w[i] <= j then
    m0[i, j] := max(m0[i-1, j], m0[i-1, j-w[i]] + v[i])
    m1[i, j] := max(m1[i-1, j], m1[i-1, j-w[i]] + v[i], m0[i-1, j-w[i]] + 2 * v[i])
else
    m0[i, j] = m0[i-1, j]
    m1[i, j] = m1[i-1, j]
end if

Как и в обычной задаче, вы можете использовать два одномерных массива, а не два двумерных массива, чтобы уменьшить использование памяти.

person Paul Hankin    schedule 18.05.2014
comment
Не отвечает на вопрос, это разновидность ранца, а не обычный ранец - person amit; 18.05.2014
comment
@amit Я не думаю, что ты прочитал полный ответ. Я представляю решение обычной задачи, чтобы я мог распространить его на задачу с двойным подсчетом очков. - person Paul Hankin; 18.05.2014
comment
Да, ты слишком хорошо это спрятал. извините, попробуйте отформатировать его лучше (начало ответа, и большая его часть относится к обычному рюкзаку ...) - кроме этого, хороший ответ (+1). - person amit; 18.05.2014
comment
PS, я думал о том же, но было бы более элегантно использовать дополнительное измерение вместо m0,m1, ИМО. - person amit; 18.05.2014
comment
Спасибо @amit, я отредактировал ответ, чтобы было понятнее, почему я сначала смотрю на обычную проблему. - person Paul Hankin; 18.05.2014
comment
Другой вариант — просто отсортировать товары по весу. Тогда результатом будет MAX(i=1 to n, 2*v[i] + m0[i-1, W-w[i]). Третье измерение не нужно. - person Niklas B.; 18.05.2014
comment
Извините, я имел в виду сортировку элементов по значению. Потому что вы всегда будете удваивать стоимость самого ценного предмета, который вы возьмете. - person Niklas B.; 18.05.2014

Почему вы не используете динамическое программирование?

http://en.wikipedia.org/wiki/Knapsack_problem#Dynamic_programming

person Andrea Sindico    schedule 18.05.2014