数组两端取数

这是完美世界的一道笔试题,题目详述忘记了,大概记得这么一个意思:
给定一个数组nums[0~n-1]; A和B分变从数组的两端交替循环选择数据,累加到自身的积分中,并且A和B都采用最优策略选择数据,求最终A和B的积分。
首先考虑考虑用深度优先算法,定义一个函数int func(int st,int len),这个函数的功能是仅考虑在数组剩余区间为[st,en]时刻及之后(之前各自的积分都重置为0),A和B的积分之差;
那么:

int func(int st,int len) { if(st>en) return 0; if(st==en) return nums[st]; /*如果A选择最左端数据,那么B可以选择新的最左端或者最右端,B会选择二者中A本轮积分差和以后积分差之和最小的那种方式*/ int t1= min(nums[st]-nums[st+1]+func(st+2,en),nums[st]-nums[en]+func(st+1,en-1)); //同样的考虑A选择最右端数据的情况下B的选择 int t2 = min(nums[en]-nums[en-1]+func(st,en-2),nums[en]-nums[st]+func(st+1,en-1)); //A会选择两种情况下最优的那个 return max(t1,t2); }

【数组两端取数】上述采用的DFS方法,会有大量的重复计算,因为对每一个(st,en)都会重复多次进入到一个func(st,en),而且栈深度会很深,极可能会超时,考虑func(st,en)的值仅与(st,en)相关,如果我们能在计算[st,en]区间上的func函数值(积分差)时已知区间[st+2,en],[st+1,en-1],[st,en-2]上的积分差值,则完全可以避免重复调用。这其实是可以做到的,采用动态规划的方法来计算区间上积分差值。如果我们倒过来看,先计算最后一轮A和B的积分差,当在不同区间上计算积分是,积分差不同,分别保存,然后后退到倒数第二轮,计算新的区间上的积分差。用dp[st][en]保存区间[st,en]上的值。最后一轮的区间长度可能是1,也可能是2。
代码如下:
#include #include using namespace std; int nums[200]; int dp[200][200]={0}; int main() { int n; cin>>n; int sum=0; for(int i=0; i>nums[i]; sum+=nums[i]; } int len=0; //分n为奇偶分类讨论下初始情况,len是区间[st,en]的长度-1 if(n%2==1) { for(int i=0; i

    推荐阅读