算法训练 逗志芃的危机
题目描述
逗志芃又一次面临了危机。逗志芃的妹子是个聪明绝顶的人,相比之下逗志芃就很菜了。现在她妹子要和他玩一个游戏,这个游戏是这样的:一共有n个数(n是偶数)写成一行,然后两个人轮流取数,每次只能从最前面或者最后面取走一个数,全部取完则游戏结束,之后每个人取走的数的和就是每个人的得分。由于逗志芃妹子很厉害,但他又不想输,所以只能找到你了,你要告诉他最多可以得到多少分。(注意,妹子智商是maxlongint所以是不会犯错的,每次的策略必然最优,而且逗志芃是先手)
输入格式
第一行一个数n,表示有n个数。第二行就是进行游戏的n个数。
输出格式
一个数,逗志凡最高得分
样例输入
2
10 20
样例输出
20
数据规模和约定
例:0<n,m<=1000,每个数不超过10000 。
题目思考
偶数个数字的数列a[N],N%2==0
逗志凡是先手,那直观的想,逗志凡每次都需要找到头尾两端的两个数中的最大的那个,即max(a[0],a[N-1])。
第二轮开始,轮到妹子选取,妹子肯定会选择最大的那个。
为了防止逗志凡短浅,只看到目前最大的,忽视了未来更大的数字,因此我们需要回溯,但是这个是我之前的想法,现在我学习他人的思想,受益良多。
第一种 动态规划
虽然题目是采用动态规划的方式求解的,但是其中还惨和有一点博弈论的思考方式
#include <iostream>
using namespace std;
int main()
{
//n代表有n个数字
int n;
cin >> n;
int nums[n];
//ans[n][n]这个二维数组,超越了我对二维数组的认知
//ans[n][n]代表从第i个数字到第j个数字,逗某人可以获得的最大数值
//最终的结果就是ans[0][n-1]
int ans[n][n];
//输入n个数字
for(int i=0; i<n ; i++)
{
cin >> nums[i];
}
//开始进行动态规划算法
//中间的ans[L][R]表示L(th)-R(th)的数字的最大值
//因为R是从右到左的方向移动的,L是从0->n-1移动的,最终是ans[0][n-1]
//所以在这里我的初始状态定义没搞明白,状态转移方程也没明白
for(int R=0;R<n;R++)
{
for(int L=R;L>=0;L--)
{
if(L==R)
{
//表示只有一个数字,只能被那个女孩子拿走
//这里不太明白
ans[L][R] = 0;
}
else if((R-L)&1) //表示还剩下偶数个数字,那就是逗某下手
{
ans[L][R] = max(nums[L]+ans[L+1][R],nums[R]+ans[L][R-1]);
}
else
{
//女孩子不会手下留情,所以留给逗志凡的会很少
ans[L][R] = min(ans[L+1][R], ans[L][R-1]);
}
}
}
cout << ans[0][n-1] << endl;
return 0;
}
状态转移方程是这道题目的关键
对于逗先生来说,每次轮到他的时候,剩下偶数个数字。然后我们的目标是让总和最大,于是肯定需要确定
ans[L][R] = max(nums[L]+ans[L+1][R],nums[R]+ans[L][R-1]);
表示要选取最前面的还是最后面的最好,反正选最大的。
然后就是女友出手
················································
说实话,对动态规划一类的题目还缺乏认识,先写到这,之后继续
················································