在这里插入图片描述

题目链接

题意:有一个长度为n的整数序列,两个人AABB轮流取数,AA先取。一个玩家每次只能从左端或者右端取任意数量连续的数,但不能两边同时取。当所有的数都被取走时游戏结束,然后统计每个人取走的数的和,作为各自的得分。两个人采取的策略都是让自己得分尽可能高,并且两个人都很机智,求AA的得分B-B的得分后的结果。
显然,最后整数序列的所有元素必定被AABB取得。
定义dp[i][j]dp[i][j]为第i到j个数,先手能取得的最大得分,Sum(i,j)Sum(i,j)表示iijj的数的和。
因为彼此都很聪明,所以dp[i][j]=Sum(i,j)dp[i][j]=Sum(i,j)-后手所能取得的分数的最大值。而先手能从左端或右端取任意数量的数,因此:
转移方程dp[i][j]=Sum(i,j)min(dp[i+1][j],dp[i+2][j],dp[j][j],dp[i][j1],dp[i][j2],dp[i][i])dp[i][j]=Sum(i,j)-min(dp[i+1][j],dp[i+2][j],\cdots dp[j][j],dp[i][j-1],dp[i][j-2],\cdots dp[i][i])(当先手取了第ii个数,此时的后手得分就是dp[i+1][j]dp[i+1][j],其他以此类推)

#include<iostream>
#include<array>
#include<algorithm>
using namespace std;
class Computer {
	array<int, 101> Data;
	array<int, 101> PrefixSum;//前缀和,用于求Sum(i,j)
	array<array<int, 101>, 101> Dp;
	array<array<bool, 101>, 101> Vis;
	int n;
	Computer() = default;
	void Init()noexcept{

		for_each(Vis.begin(), Vis.end(), [](array<bool, 101> & Array)->void {
			Array.fill(false);
			});
		for_each(Dp.begin(), Dp.end(), [](array<int, 101> & Array)->void {
			Array.fill(0);
			});

		PrefixSum[1] = Data[1];
		for (int i = 2; i <= n; ++i) {
			PrefixSum[i] = PrefixSum[i - 1] + Data[i];
		}
	}
	int Dfs(int From, int To) {
		if (Vis[From][To]) {
			return Dp[From][To];
		}
		Vis[From][To] = true;
		int&& M = 0;
		for (int i = From + 1; i <= To; ++i) {
			M = min(M, Dfs(i, To));
		}
		for (int i = From; i < To; ++i) {
			M = min(M, Dfs(From, i));
		}
		return (Dp[From][To] = PrefixSum[To] - PrefixSum[From - 1] - M);
	}
public:
	static Computer computer;
	bool Input() {
		cin >> n;
		if (!n) {
			return false;
		}
		for (int i = 1; i <= n; ++i) {
			cin >> Data[i];
		}
		return true;
	}
	int getAns() {
		Init();
		int&& Temp = Dfs(1, n);
		return Temp - (PrefixSum[n] - Temp);
	}
};
Computer Computer::computer;
int main() {
	while (Computer::computer.Input()) {
		cout << Computer::computer.getAns() << endl;
	}
	return 0;
}
posted on 2020-01-15 23:38  SCU_GoodGuy  阅读(178)  评论(0)    收藏  举报