題意:
題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=6199
AB兩個人玩遊戲,n個數,從左到右依次選取。第一個人可以選擇1個或者2個數,假設某一輪一個人選擇了k個數,那麼下一輪另一個人就要選擇k個或者k+1個數,如果剩下的數字不夠就不能選擇,遊戲終止。此時比較A-B的大小,問兩個人都採取最優策略,都儘可能讓自己所獲得的數的總和大,最後A-B的最大值是多少。
思路:
真的菜,網絡賽時候有正確思路,結果細節沒搞清楚,最後愣是沒調出來。
dp[i][j][0/1]表示當前這一輪是0(A)或1(B)選擇數字,還剩下j個數,馬上將要選擇i個數的A-B的最大值。然後從後往前倒着推,最後答案就是max(dp[1][n][0], dp[2][n][0])。
狀態轉移方程爲:
dp[i][j][0] = min(dp[i][j-i][1], dp[i+1][j-i][1])+val;
dp[i][j][1] = max(dp[i][j-i][0], dp[i+1][j-i][0])+val;
其中,val爲選擇的i個數的總和,這裏有一點一定要注意,0對應min,而1對應max,不能搞反。因爲這是從後往前倒推的,當前是0面對的一定是之前的1所留下來對1最好的局面,也就是min。
噁心的爆內存,要改成滾動數組。
還要注意很多細節問題,詳見代碼。
代碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF = 2e9 + 1;
const int MAXN = 2e4 + 10;
int a[MAXN], sum[MAXN], dp[2][MAXN][2];
int main() {
//freopen("in.txt", "r", stdin);
int T;
scanf("%d", &T);
while (T--) {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
sum[i] = sum[i - 1] + a[i];
}
if (n == 1) {
printf("%d\n", a[1]);
continue;
}
for (int i = 199; i >= 1; i--) {
for (int j = i; j <= n; j++) {
int l = n - j, r = n - j + i;
if (j - i < i) {
dp[i % 2][j][0] = sum[r] - sum[l];
dp[i % 2][j][1] = -(sum[r] - sum[l]);
}
else {
dp[i % 2][j][0] = dp[i % 2][j - i][1] + sum[r] - sum[l];
dp[i % 2][j][1] = dp[i % 2][j - i][0] - (sum[r] - sum[l]);
if (i + 1 <= 199 && j - i >= i + 1) {
dp[i % 2][j][0] = min(dp[i % 2][j][0], dp[(i + 1) % 2][j - i][1] + sum[r] - sum[l]);
dp[i % 2][j][1] = max(dp[i % 2][j][1], dp[(i + 1) % 2][j - i][0] - (sum[r] - sum[l]));
}
}
}
}
int ans = max(dp[0][n][0], dp[1][n][0]);
printf("%d\n", ans);
}
return 0;
}