题意:
有n个带权球,A和B两个人,A先手拿球,一开始可以拿1个或2个,如果前一个人拿了k个,那么当前的这个人只能那k或k+1个,如果当前剩余的球不足,那么剩下的球都作废,游戏结束。假设两个人都是聪明人,每个人都会想方设法设获得比对方更多的球。问A最多能B多拿多少。
分析:
可以将先手取1n的最优值看做一个问题,先手取2n又是一个新的子问题,这样递归下去,便可求解。
设(f[i][k])表示从第i个开始,可以取k或k+1个,先手取比对方多出的最大值(可能是负数)。
考虑用记忆化搜索:
(f[i][k] = max(sum[i, i + k - 1] + f[i + k][k], sum[i, i + k] + f[i + k + 1][k + 1]))
注意递归边界及条件。
记忆化搜索的常数极大,考试时明明写的正解都被卡飞了,改了一个小小的初始化就A了,还是多写递推吧。
code
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<ctime>
#include<vector>
#include<queue>
#include<map>
using namespace std;
namespace IO{
template<typename T>
inline void read(T &x){
T i = 0, f = 1; char ch = getchar();
for(; (ch < '0' || ch > '9') && ch != '-'; ch = getchar());
if(ch == '-') f = -1, ch = getchar();
for(; ch >= '0' && ch <= '9'; ch = getchar()) i = (i << 3) + (i << 1) + (ch - '0');
x = i * f;
}
template<typename T>
inline void wr(T x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) wr(x / 10);
putchar(x % 10 + '0');
}
}using namespace IO;
const int N = 2e4 + 50, OO = 2e9 + 5;
int T, n;
int a[N], sum[N];
typedef pair<int,int> P;
int f[N][250];
int vst[N][250], vt;
inline void chkmx(int &x, const int &y){
if(x < y) x = y;
}
inline int F(int x, int k){
// if(f[x][k] != -OO) return f[x][k];
if(vst[x][k] == vt) return f[x][k];
vst[x][k] = vt;
if(x > n - k + 1)
return f[x][k] = 0;
int ret = -OO;
if(x <= n - (k + 1) + 1)
chkmx(ret, sum[x + k] - sum[x - 1] - F(x + k + 1, k + 1));
chkmx(ret, sum[x + k - 1] - sum[x - 1] - F(x + k, k));
return f[x][k] = ret;
}
int main(){
freopen("game.in", "r", stdin);
freopen("game.out", "w", stdout);
read(T);
while(T--){
read(n);
sum[0] = 0;
for(register int i = 1; i <= n; i++) read(a[i]), sum[i] = sum[i - 1] + a[i];
// for(register int i = 1; i <= n; i++)
// for(register int j = 0; j <= 200; j++)
// f[i][j] = -OO;
vt++;
wr(F(1, 1));
putchar('
');
}
return 0;
}