Light OJ 1031 - Easy Game(区间DP)
题目大意:
给你一个n,代表n个数字,现在有两个选手,选手A,B轮流有有一次机会,每个选手一次可以得到一个或者多个数字,从左侧或者右侧,但是不能同时从两边取数字,当所有的数字被取完,那么游戏结束。然后计算每个选手所得到数字的总和,每个选手都尽量让自己的分数比较多,选手A先开始取数。假设每个选手取得数字都是最优的,问A最多比B多多少分数,、
题目分析:
记忆化搜索,区间DP。
dp[该谁取了][左区间L][右区间] = 所能取到的最大值。
做下简单的预处理,得到区间L-R之间的和。
然后状态转移 dp[cur][L][R] = max(dp[cur][L][R], sum[L][K] + sum[K+1][R] - dp[cur^1][L][K] );
然后再反过来做一次DP, 总体求最大就行了。
=====================================================================================================
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include<map> using namespace std; typedef long long LL; const int INF = 1e9+7; const int MAXN = 255; int a[MAXN], dp[2][MAXN][MAXN]; int sum[MAXN]; int DFS(int cur,int L,int R) { if( dp[cur][L][R] != -INF ) return dp[cur][L][R]; if(L > R) return 0; if(L == R) return dp[cur][L][R] = a[L]; for(int i=L; i<=R; i++) { int A = sum[i]-sum[L-1];///所选取的区间 int B = sum[R]-sum[i];///丢弃的区间总和 dp[cur][L][R] = max(dp[cur][L][R],A + B - DFS(cur^1,i+1,R) ); } for(int i=R; i>=L; i--) { int A = sum[R] - sum[i-1]; int B = sum[i-1] - sum[L-1]; dp[cur][L][R] = max(dp[cur][L][R],A + B - DFS(cur^1,L,i-1)); } return dp[cur][L][R]; } int main() { int T, cas = 1, n; scanf("%d", &T); while(T --) { scanf("%d", &n); memset(sum, 0, sizeof(sum)); for(int i=0; i<=n; i++) for(int j=0; j<=n; j++) dp[0][i][j] = dp[1][i][j] = -INF; for(int i=1; i<=n; i++) { scanf("%d", &a[i]); sum[i] = sum[i-1] + a[i]; } int ans = DFS(0, 1, n); printf("Case %d: %d\n", cas++, 2*ans - sum[n]); } return 0; } /* 3 3 1 2 3 */