传送门
题意:给定一个长为n的整数序列(n<=1000),由A和B轮流取数(A先取)。每个人可从序列的左端或右端取若干个数(至少一个),但不能两端都取。所有数都被取走后,两人分别统计所取数的和作为各自的得分。假设A和B都足够聪明,都使自己得分尽量高,求A的最终得分。
思路:看视频讲的这道题,喵啊,按照他的思路写了出来。取数肯定是取一段连续的序列,剩下一段连续的序列。
首先定义F[l][r] : [l,r]区间 a-b的最大值(a代表a取得最大和,b代表b取得最大和)。为何这样定义,这样我们会得到两个方程 a-b=F[1][n] 和 a+b=allsum 这样我们就可以得出 a=(F[1][n]+allsum)/2;
面对[l,r]区间我们有3种选择。sum[]为前缀和数组
- 全取了,F[l][r] = sum[r] - sum[l-1].
- 从左边取一段 F[l][r] = sum[l'-1] -sum[l] - F[l'][r] (解释一下为什么是减法呢,因为剩下来的区间 [l'][r] b也会选择最优策略,使得b-a最大,所以a就只能得到 -F[l'][r] 即 b选择下的 a-b的贡献了)
- 从右边取一段 F[l][r] =sum[r] - sum[r'] - F[l][r']
更新就是在上面三种情况取最大即可,我们可以分析出,枚举长度len,枚举起点l,枚举断点k 是O(n^3)的复杂度,显然过不了。
这就需要耍点手段了。
我们看第三种情况下的递推式 转化 F[l][r] =sum[r] - (sum[r'] + F[l][r'])
我们设P[l][r] = sum[r] + F[l][r], 再设一个Mi[l][r] = min{ P[l][x] | l<=x<=r },我们想要F[l][r]大,不就是想要P[l][r]最小嘛,所以我们Mi[l][r]的转移也很简单,Mi[l][r]=min(Mi[l][r-1],sum[r]+F[l][r]); (这里P数组没有实际作用,为了简单说明) 第三种情况的转移其实就 F[l][r]=max(F[l][r],sum[r]-Mi[l][r-1]);
同理对于第二个递推式 转化 ( sum[l'-1] - F[l'][r] )-sum[l-1]
这里我们设T[l][r]=sum[l-1] - F[l][r] ) , 用Mx[l][r] = max{ T[x][r] | l<=x<=r }维护,最后我们也可以推得 F[l][r]=max(F[l][r],Mx[l+1][r]-sum[l-1]);
最后代码:
#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define fi first
#define se second
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define sc(n) scanf("%d",&n)
#define SC(n,m) scanf("%d %d",&n,&m)
#define SZ(a) int((a).size())
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define drep(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const double PI=acos(-1.0);
const double eps=1e-9;
const int maxn=1e3+5;
int a[maxn],sum[maxn],F[maxn][maxn];
int Mx[maxn][maxn],Mi[maxn][maxn],T_T,n;
il void init(int n) {
for(int i=1; i<=n+2; ++i) {
sum[i]=0;
for(int j=1; j<=n+2; ++j) {
Mx[i][j]=-inf,Mi[i][j]=inf;
}
}
}
int main() {
sc(T_T);
while(T_T--) {
sc(n);
init(n);
rep(i,1,n) sc(a[i]),sum[i]+=sum[i-1]+a[i];
for(int len=1; len<=n; ++len) {
for(int l=1; l+len-1<=n; ++l) {
int r=l+len-1;
F[l][r]=sum[r]-sum[l-1];
F[l][r]=max(F[l][r],sum[r]-Mi[l][r-1]);
F[l][r]=max(F[l][r],Mx[l+1][r]-sum[l-1]);
Mi[l][r]=min(Mi[l][r-1],sum[r]+F[l][r]);
Mx[l][r]=max(Mx[l+1][r],sum[l-1]-F[l][r]);
}
}
printf("%d\n",(F[1][n]+sum[n])/2);
}
return 0;
}