题意 : 两个人轮流拿物品,每一个物品都有一个价值,第 i 次拿 k 个那么 第 i + 1 次 可以拿 k + 1 或者 k 个。每个人都采取最有策略问你最后两个人的差。
题解 : 首先我们发现每一次只能拿k 或者 k + 1 个,这样的话我们就可以发现这样的一个性质 若一第 i 个开始拿,这一次拿的个数不能超过 sqet (2 * i) + 1个。这样的话我们就可以定义dp 状态了 dp (i,j,k) 表示从第 k 个人从 第 i个开始拿 ,拿走 j 个 第一个人拿走的减去第二个人所拿走的差的值 k 只有 0 1两个值分别表示第一个人拿还是第二个人拿,每个人的目的不一样所以dp的转移稍微有点不同,我们发现dp的状态是从后往前转移的这一点很关键。
dp(i,j,0) = min (dp(i + j,j,1),dp[i + j,j + 1,1)) + sum[i + j - 1] - sum[i - 1];
dp(i,j,1) = max (dp(i + j,j,0),dp[i + j,j + 1,0) - (sum[i + j - 1] - sum[i - 1]); sum 是前缀和
因为前面的人要受到选完之后要收到后面的那个人的控制并且 0 的目的是使得这个答案尽量大,1的目的是使得答案尽量小 所以两个的dp转移有所不同类似于敌对搜索的状态。
还有这个题目竟然卡内存....... 表示无语........ 可以是用指针解决或者第一维压缩一下 也可以的代码里没有压缩内存 。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn = 20005;
int dp[maxn][202][2] = {0};
int pre[maxn] = {0};
int n;
int main () {
ios_base :: sync_with_stdio(false);
int T;
cin >> T;
while (T--) {
cin >> n;
memset (dp,0,sizeof(dp));
pre[0] = 0;
for (int i = 1;i <= n; ++ i) {
cin >> pre[i];
pre[i] += pre[i - 1];
}
pre[n + 1] = pre[n + 2] = pre[n];
for (int i = n;i >= 1;-- i) {
int u = sqrt (i + i) + 1;
int remain = n - i + 1;
int lim = min (remain,u);
for (int j = 1;j <= lim; ++ j) {
if (j == remain) {
dp[i][j][0] = pre[i + j - 1] - pre[i - 1];
dp[i][j][1] = pre[i - 1] - pre[i + j - 1];
}
else {
if (i + j + j - 1 <= n) {
dp[i][j][0] = dp[i + j][j][1];
dp[i][j][1] = dp[i + j][j][0];
if (i + j + j <= n) {
dp[i][j][0] = min (dp[i][j][0],dp[i + j][j + 1][1]);
dp[i][j][1] = max (dp[i][j][1],dp[i + j][j + 1][0]);
}
}
dp[i][j][0] += (pre[i + j - 1] - pre[i - 1]);
dp[i][j][1] -= (pre[i + j - 1] - pre[i - 1]);
}
}
}
// cout << dp[1][1][0] << ' ' << dp[1][2][0] << ' ';
cout << max (dp[1][1][0],dp[1][2][0]) << endl;
}
return 0;
}