有一排正数,玩家A和玩家B都可以看到。

每位玩家在拿走数字的时候,都只能从最左和最右的数中选择一个。

玩家A先拿,玩家B再拿,两人交替拿走所有的数字,

两人都力争自己拿到的数的总和比对方多。请返回最后获胜者的分数。

 

例如:

5,2,3,4

玩家A先拿,当前他只能拿走5或者4。

如果玩家A拿走5,那么剩下2,3,4。轮到玩家B,此时玩家B可以选择2或4中的一个,…

如果玩家A拿走4,那么剩下5,2,3。轮到玩家B,此时玩家B可以选择5或3中的一个,…

思路:

动态规划,设定两个状态,

分别为f(i,j)和 s(i,j)分别表示在 序列i..j 之间,先手能得到的最大数目以及后手计算后剩下的最大数目。

所以先手f(i,j) = max(arr[i]+s(i+1,j),arr[j]+s(i,j-1)) 表示目的为求最大的情况

后手剩下的为  s(i,j)= min(f(i+1,j),f(i,j-1);  而后手玩家为了拿到更多,所以他会选择,留给对方最少的那种情况。

通过这种相互依赖,我们可以写出递归函数,并设定递归边界,

在先手情况下f(i,j),如果 i==j 表示只剩下最后一个,那么 就直接返回这个数。

 在后手情况下s(i,j),如果 i==j 表示只剩下最后一个,那么 就直接返回0。因为后手玩家已经取完了,所以留给先手玩家的就为0;

接着,通过画出依赖关系,我们可以发现f和s的相互关系,并且很多情况的递归都是重复计算的,所以这里就出现了无后效性的情况,即后面的计算都不会改变之前已经计算好的。就可以根据这个写出动态规划。

代码:

#include<iostream>
#include<stdio.h>
using namespace std;
int arr[1000] = {0}; 

int N;
//设置两个状态图 
int dps[1000][1000]={0};
int dpf[1000][1000]={0};

int main() {
	
	scanf("%d",&N);
	for(int i=0;i<N;i++){
		scanf("%d",&arr[i]);
	}
	
	
	for (int j=0;j<N;j++){
		//先手状态初始化 
		dpf[j][j] = arr[j];
		
		for (int i = j-1;i>=0;i--){
			//先手状态和后手状态依次计算 
			dpf[i][j] = max(arr[i]+ dps[i+1][j],arr[j]+dps[i][j-1]);
			dps[i][j] = min(dpf[i+1][j],dpf[i][j-1]);
		}
	}
	
	printf("%d\n",dpf[0][N-1]);

	return 0;
}