Задача взята с сайта e-olymp
Условие
Однокопеечные монетки разложены в стопки (в стопках может быть различное количество монет), а стопки поставлены на столе в ряд слева направо. Двое противников по очереди делают ходы. Ход состоит в том, что один из игроков берет слева несколько стопок подряд, не меньше одной, но и не больше, чем перед этим взял его соперник. Первый игрок своим первым ходом берет не более $ K$ стопок. Игра заканчивается, когда стопок не остается. Требуется найти максимальное число монет, которое может получить первый участник после окончания игры, если второй игрок тоже старается ходить так, чтобы получить как можно больше монет.
Входные данные
В первой строке находится сначала число стопок $ N,$ за ним идут $ N$ чисел, задающих количество монет в стопках слева направо, а затем число $ K.$ Все числа в строке разделены пробелами.
$ 1 \leqslant N \leqslant 180, 1 \leqslant K \leqslant 80,$ количество монет в стопке — не менее $ 1$ и не более $ 20 000$.
Выходные данные
Вывести одно число — максимальное количество монет, которое заведомо может получить первый игрок.
Тесты
№ | Inputs | Outputs |
1 | 3 4 9 1 3 | 14 |
2 | 4 1 2 2 7 3 | 5 |
3 | 5 3 4 8 1 7 2 | 18 |
4 | 12 67 8 6 12 6 90 54 89 145 32 45 65 4 | 357 |
5 | 14 32 53 5 52 9 8 17 5 87 44 51 12 15 56 10 | 312 |
6 | 14 76 112 3 1 98 4 172 33 65 90 2 71 18 32 14 | 777 |
Код
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
#include <iostream> #include <vector> #include <limits.h> using namespace std; int sum[190][90][3]; int solution(vector<int>&arr, int l, int k, int p){ if(sum[l][k][p]){ return sum[l][k][p]; } int ans = 0; if(arr.size() - l <= k){ for(int i = l; i < arr.size(); i++){ ans += arr[i]; } if(p == 2){ sum[l][k][p] = 0; return sum[l][k][p]; } sum[l][k][p] = ans; return sum[l][k][p]; } if(p == 1){ int temp = 0; for(int i = 1; i <= k; i++){ temp += arr[(l-1) + i]; ans = max(ans, temp + solution(arr, l+i, i, 3-p)); } sum[l][k][p] = ans; return sum[l][k][p]; } else{ int ans = INT_MAX; for(int i = 1; i <= k; i++){ ans = min(ans, solution(arr, l+i, i, 3-p)); } sum[l][k][p] = ans; return sum[l][k][p]; } } int main() { int n, k, a; vector<int> coins; cin >> n; for(int i = 0; i > a; coins.push_back(a); } cin >> k; cout << solution(coins, 0, k, 1); return 0; } |
Решение
Анализируя задачу, становится понятно, что ее можно решить методами динамического программирования, а именно трехмерная динамика по параметрам: количество уже взятых стопок — l, количество стопок, которые можно взять kи какой игрок сейчас ходит p. Каждый игрок старается ходить оптимально, тогда для первого игрока задача взять максимальное количество монет, а для второго игрока задача оставить наименьшее количество монет оппоненту. Решение исполнено с помощью рекурсии, где терминальный случай-когда в массиве осталось количество стопок, меньшее или равное тому, сколько игрок может взять. Также стоит проверять высчитывалась ли раньше данная позиция, иначе задача не зайдет по времени.