@Rushpil

Как будет выглядеть алгоритм?

Дана задачка: Человек и робот играют в игру.Первый ход делает человек. На столе лежат N карточек с цифрами(на карточках написаны цифры). Робот и человек берут карточки поочередно.Когда наступает ход человека,то у него есть 2 возможности: взять 1 карточку или пропустить ход.Один раз за всю игру человек может взять за 1 ход сразу две карточки.У робота таких возможностей нету,он берет каждый раз только одну карточку и не может пропускать ходы. Найти выигрышную для человека траекторию,т.е. такой набор ходов,при котором все взятые человеком карточки давали бы большую сумму,чем те карточки,которые взял робот.
Ввод:
1 2 -6 7 4 3
Вывод(карточки с цифрами):(Сначала человек пропускает ход,робот берет: 1; затем человек берет:2; робот берет -6;
человек берет 2 карточки: 7,4; робот берет:3)
2,7,4
Помогите с алгоритмом к этой задаче.
Вот мой алгоритм,но я не думаю, что он верный:
1)Рассматриваем простые случаи:
A) когда 1 карточка, то человек ее берет.
Б) когда 2 карточки,если обе карточки не отрицательные,то человек берет обе,если хотя бы 1 отрицательная,то берет максимальную из них
2)Обычный случай(при N>2):
линейно сравнивать подряд идущие карточки между собой,но тут возникает вопрос:может быть так,что существует несколько пар подряд идущих карточек ,суммы которых равны.Как поступать в этом случае и находить оптимальную траекторию ?
Буду рад если поможете разобраться с данной задачей .
  • Вопрос задан
  • 199 просмотров
Решения вопроса 1
wataru
@wataru Куратор тега Алгоритмы
Разработчик на С++, экс-олимпиадник.
Решение через динамическое программирование. F(k) - лучшая сумма, которую можно набрать из карточек, начиная с k, когда можно один раз взять 2 карточки, g(k) - тоже самое, но 2 карточки брать уже нельзя.

g(k) = max(a[k]+g(k+2),g(k+1)); - или вы берете первую карточку, то робот берет следующую и далее задача повторяется. Второй вариант - вы пропускаете эту карточку, тогда ее берет робот. Задача повторяется со следующей карточки.

f(k) = max(a[k]+a[k+1]+g(k+3), a[k] + f(k+2), f(k+1));
- так же, мы либо берем 2 карточки, либо 1, либо 0. Если мы берем 2, то в будущем 2 уже брать нельзя, придется брать g(k+3).

Вот решение на C++ (вместо 2 функций используется флаг, можно ли брать 2 карточки)

vector<int> dp[2]; // заполнен MIN_INT, размером с n.
vector<int> take[2]; // размером с n.
int f(const vector<int> &a, int k, int can_take_two) {
   if(k >= n) return 0;
   if (dp[k][can_take_two] > MIN_INT) return dp[k][can_take_two];
   // skip;
   int best = f(a, k+1, can_take_two);
   take[k][can_take_two] = 0;
   // take 1;
   int cur = f(a, k+2, can_take_two) + a[k];
   if (cur > best) {
     best = cur;
     take[k][can_take_two] = 1;
   }
   if (can_take_two && k + 1 < n) {
      cur = f(k+3, 0) + a[k] + a[k+1];
      if (cur > best) {
        best = cur;
        take[k][can_take_two] = 2;
      }
   }
   dp[k][can_take_two] = best;
   return best;
}
//...
// f(a, 0, 1) - вернет лучшую сумму.

//Для восстановления ответа можно воспользоваться массивом take, который хранит для каждого состояния, сколько карточек брать:

k = 0; ct = 1;
while (k <n) {
  num = take[k][ct];
  for (j = 0; j < num; ++j) {
    cout << a[k+j] << " ";
  }
  if (num == 2) ct = 0;
  k += num + 1;
}


Если нужна только сумма, а не сам набор чисел, то можно развернуть рекурсию в 2 вложенных цикла (один от n до 0, другой от 0 до 1) и хранить только 3 последних строчки массива dp.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
longclaps
@longclaps
Вот решение. Не уверен, что сможешь разобраться - но попробуй
l = [1, 2, -6, 7, 4, 3]
le = len(l)
step0, step1, step2 = [None, []], [None, None], [None, None]
l += [0, 0]  # добьём незначащими нулями
for i in range(le):
    x, y, z = l[i:i + 3]
    for j, pos in enumerate(step0):
        if pos is None:
            continue
        newpos = [*pos, -x]
        if step1[j] is None or sum(step1[j]) < sum(newpos):
            step1[j] = newpos
        newpos = [*pos, x, -y]
        if step2[j] is None or sum(step2[j]) < sum(newpos):
            step2[j] = newpos
    step0, step1, step2 = step1, step2, [[*pos, x, y, -z], None]
best = max((pos for step in (step0, step1, step2) for pos in step if pos is not None), key=sum)
print(best[:le])
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы