Loading

luogu P1430 序列取数

题面

题目大意

给定一个长为 \(n\) 的整数序列 ,由 \(A\)\(B\) 轮流取数(\(A\) 先取)。每个人可从序列的左端或右端取若干个数(至少一个),但不能两端都取。所有数都被取走后,两人分别统计所取数的和作为各自的得分。假设 \(A\)\(B\) 都足够聪明,都使自己得分尽量高,求 \(A\) 的最终得分。

\(T\leq 100, n\leq1000\)

solution

曾老师:“根据题目条件需要啥设啥"

回到题中

题目中所说每个人只能从两端取,所以在序列的两端搞点事情

区间 \(dp\) :考虑大区间如何由小区间转移得到(我方:A, 敌方:B)

\(L[i][j]\) 表示 \([i,j]\) 区间内,\(a[i]\) 必须时选所能获取的最大得分

\(R[i][j]\) 表示 \([i, j]\) 区间内,\(a[j]\) 必须选所能获取的最大得分

\(S[i][j] = \sum_{k = i}^{k = j} a[k]\)

这里的 \(L, R\) 是全局的状态,可以指 A, 也可以指 B

转移:(由于是 \(A\)\(B\) ) 足够聪明,要用到博弈思想

\(L[i][j]\) 的转移为例

考虑下一步该怎么走

下一步我方取 \(a[i + 1]\)

\(L[i][j] = a[i] + L[i + 1][j]\)

下一步我方取 \(a[j]\)

因为 \(a[i]\) 必须取,因为 \(a[i]\)\(a[j]\) 不连续,所以不能同时取走,这个状态显然是不合法的

下一步对手取 \(a[i + 1]\)

\(L[i][j] = a[i] + S[i][j] - L[i + 1][j]\)

\(A\) 的得分等于总分减去对手的得分

下一步对手取 \(a[j]\)

\(L[i][j] = a[i] + S[i][j] - R[i + 1][j]\)

同上

\(R\) 的转移可以根据上面类比过去

初始化

\(L(i, i) = R(i, i) = a[i]\)

转移方程

\[L(i,j) = a[i] + max\begin{cases}L(i + 1, j)\\ S(i + 1, j) + min\begin{cases}-L(i + 1, j)\\ -R(i +1, j)\end{cases}\end{cases} \]

\[R(i,j) = a[j] + max\begin{cases}R(i, j-1)\\ S(i, j-1) + min\begin{cases}-L(i, j - 1)\\ -R(i, j - 1)\end{cases}\end{cases} \]

\(Ans:max(R[1, n],~~L[1, n])\)

复杂度: \(n^2\)

这样就可以取得 100 分的好成绩

考虑优化 区间小技巧

可以把 \(j\) 维滚掉

改一下状态就好了

\(L(i,j)\) 表示此时的区间为 \([i,i+j]\) 且第 \(i\) 个数必须取

\(R(i,j)\) 表示此时的区间为 \([i,i+j]\) 且第 \(i+j\) 个数必须取

转移就把区间表示稍微改一下就好了就成了

\[L(i,j) = a[i] + max\begin{cases}L(i + 1, j - 1)\\S(i + 1, i+j) + min\begin{cases}-L(i + 1, j - 1)\\-R(i +1, j - 1)\end{cases}\end{cases} \]

\[R(i,j) = a[i + j] + max\begin{cases}R(i, j-1)\\S(i, i+ j-1) + min\begin{cases}-L(i, j - 1)\\-R(i, j - 1)\end{cases}\end{cases} \]

答案就成了 \(\max(L(1,n-1),R(1,n-1))\)

code

/*
work by:Ariel
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define ll long long
using namespace std;
const int N = 1000 + 5;
int read() {
  int x = 0, f = 1; char c = getchar();
  while(c < '0' || c > '9') {if(c == '-')  f = -1;c = getchar();}
  while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
  return x * f;
}
int L[N], R[N], S[N], T, a[N], n;
int main() {
  T = read();
  while (T--) {
  	n = read();
  	for (int i = 1; i <= n; i++) {
	   a[i] = read();
	   S[i] = S[i - 1] + a[i];
	   L[i] = R[i] = a[i];
	}
	for (int j = 1; j < n; j++) {
	   for (int i = 1; i + j <= n; i++) { 
	       R[i] = a[i + j] + max(R[i], S[i + j - 1] - S[i - 1] - max(L[i], R[i]));
	   	   L[i] = a[i] + max(L[i + 1], S[i + j] - S[i] - max(L[i + 1], R[i + 1]));
	   }
	}
	printf("%d\n", max(L[1], R[1]));
  }
  return 0;
}
posted @ 2021-07-17 21:13  Dita  阅读(64)  评论(0编辑  收藏  举报