题干:
有一个长度为N的整数序列,Alice和Bob轮流取数,Alice先取。每次玩家只能从左端或者右端
取一个或多个数,但不能两端都取。所有数都被取走后游戏结束,然后统计每个人取走的所有数之和,
作为各自的得分。两个人采取的策略都是让自己的得分尽量高,并且两个人都足够聪明。
Input
输入第一行为组数T(T<=10)。
对于每组数据第一行为一个整数N(1<=N<=100),第二行为给定的整数序列。
整数的绝对值<=100
Output
对于每组数据,输出Alice和Bob都采取最优策略的情况下,Alice减去Bob的得分后的结果。
Sample Input
2
4
4 -10 -20 7
4
1 2 3 4
Sample Output
7
10
Hint
对于第一组数据: 第一步Alice从左端取1个数,第二步Bob从右端取一个数。
第三步Alice从左端取一个数, 第四步只剩下一个数被Bob取走。
所以,Alice的得分减去Bob的得分为7。
对于第二组数据:Alice直接将全部数取走,所以Alice的得分减去Bob的得分为10。
题目大意:
两个题是一样的题干,只是输入格式是不同的,Uva的是EOF结束,HRBUST的是输入样例个数t。
解题报告:
可以知道整体石子的总和一定的,所以一个人的得分越高,另一个人的得分就越低。不管怎么取任意时刻游戏的状态都是原始序列的一段连续子序列(即被玩家取剩下的序列)。
这里就可以用记忆化搜索了,用dp[i][j]表示在剩下i~j区间数字,在双方都采取最优策略的情况下,先手得分最大值。
d[i][j] = sum[i][j] - min(dp(i + k, j), dp(i, j - k)); (1 <= k <= j - i + 1) ( sum[i][j] 表示i到j的前缀和之差)
AC代码:
#include
#define ll long long
using namespace std;
const int INF = 0x3f3f3f3f;
ll dp[205][205];
ll sum[205],a[205];
ll dfs(int l,int r) {
if(dp[l][r] != -1) return dp[l][r];
if(l == r) return dp[l][l]=a[l];
// ll maxx = sum[r] - sum[l-1];
ll maxx = -INF;
for(int i = l+1; i<=r; i++) {
maxx = max(maxx , sum[r] - sum[l-1] - dfs(i,r));
}
for(int i = r-1; i>=l; i--) {
maxx = max(maxx , sum[r] - sum[l-1] - dfs(l,i));
}
// printf("dp[%d][%d] = %lld\n",l,r,maxx);
maxx = max(maxx,sum[r] - sum[l-1]);
return dp[l][r] = maxx;
}
int main()
{
int t,n;
cin>>t;
while(t--) {
scanf("%d",&n);
memset(sum,0,sizeof sum);
memset(dp,-1,sizeof dp);
for(int i = 1; i<=n; i++) scanf("%lld",&a[i]),sum[i] = sum[i-1] + a[i];
printf("%lld\n",2*dfs(1,n) - sum[n]);
}
return 0 ;
}
//18:04 - 18:17
总结:
这样是可以AC的,或者直接用这一句
// ll maxx = sum[r] - sum[l-1];
总之,就是要把“一次全取光”这种情况也考虑进去。