题目解读
原题链接: 牛客网 2016校招真题在线编程
题目描述
有一个整型数组A,代表数值不同的纸牌排成一条线。玩家a和玩家b依次拿走每张纸牌,规定玩家a先拿,玩家B后拿,但是每个玩家每次只能拿走最左或最右的纸牌,玩家a和玩家b都绝顶聪明,他们总会采用最优策略。请返回最后获胜者的分数。
给定纸牌序列A及序列的大小n,请返回最后分数较高者得分数(相同则返回任意一个分数)。保证A中的元素均小于等于1000。且A的大小小于等于300
输入描述
[1,2,100,4],4
输出描述
101
题意理解
这是一条博弈题,题目中说明了A、B两个人不断取牌来计分的过程。注意这里有一个先手和后手转换的关系。A、B两个对象,既有做先手的时刻,又有做后手的时刻,比如对于序列1,2,100,4,在时刻1,对于该序列是A先手,而一旦A选取1后,在时刻2,对于序列2,100,4,A又变为了后手,因为此刻是由B来进行挑选。同理,B也有先手和后手的时刻。
算法分析
考虑到先手和后手的对应关系,我们设置两个数据结构
First[a][b]:表示对于字符串num[a]-num[b]采用先手能够取得的分数;
Second[a][b]:表示对于字符串num[a]-num[b]采用先手能够取得的分数;
那么First[a][b] = max(A[a]+S[a+1][b],A[b]+S[a][b-1]);
Second[a][b]=min(First[a+1][b],First[a][b-1])
有一个需要注意的地方在于,如何确定循环中变量的先后顺序,
我自己的感觉是要根据状态转移方程来判断:
比如现在我让行代表a,而列代表b,则对于一个(a,b)来说,要想求得它,必须要先拿到(a+1,b)和(a,b-1)两个坐标,那么从几何角度来看,就是从左下角开始向右上角生成,所以对于a来说是要相减,而对于b来说是要相加。
代码
class Cards {
public:
int cardGame(vector<int> A, int n) {
int **F = new int*[n];
int **S = new int*[n];
for(int i=0;i<n;i++){
F[i]=new int[n];
S[i]=new int[n];
}
for(int r=0;r<n;r++){
F[r][r]=A[r];
S[r][r]=0;
}
for(int r=0;r<n;r++){
for(int l=r-1;l>=0;l--){
F[l][r] = max(A[l]+S[l+1][r],A[r]+S[l][r-1]);
S[l][r] = min(F[l+1][r],F[l][r-1]);
}
}
int result = max(F[0][n-1],S[0][n-1]);
delete[] F;
delete[] S;
return result;
}
};