纸牌博弈 改DP

这个题目刷过一次 没记住地址
有一串纸牌 只能从纸牌两边抽取扑克,有两名玩家,假设两名玩家都绝顶聪明会选择,会选择最优的情况,抽取的纸牌面值累加为分数,求获胜者的分数和

输入:
4
2 7 100 20
输出:
102

暴力递归

先思考抽牌的情况

  1. 从左边抽 那么第二个人就从剩下的部分抽
  2. 从右边抽 ------------------------------------------

由于两个人都是绝顶聪明 ,所以我们要考虑从左右抽取的时候 哪边更加合适,max(arr[L]+S(arr,L+1,R) , arr[R]+S(arr,L,R-1))

/* 
拿纸牌 3,7,100,20
两个人只能从左右两边拿  
返回获胜者的分数 
*/ 
#include <bits/stdc++.h>
using namespace std;

// 因为我们只求 开始时先手的人 最高分 所以 当先手F之后是后手 后手肯定只剩最小的情况
// (两个人都绝顶聪明) 
int S(int arr[],int l,int r);
int F(int arr[],int l,int r){
    
     //先手
	if(l==r)return arr[l];
	return max( arr[l]+S(arr,l+1,r) , arr[r]+S(arr,l,r-1) ); 
	//假设我先手 拿了最好的情况 那么轮到下一个人也会拿最好的结果,所以S函数给我的是最小值,不过我挑选的是F函数的最大值
}
int S(int arr[],int l,int r){
    
    //后手
	if(l==r)return 0;
	return min(F(arr,l+1,r),F(arr,l,r-1));
}
int main()
{
    
    
	int n;
	cin>>n;
	int num[5];
	for(int i=1;i<=n;i++){
    
    
		cin>>num[i];
	}
	cout<<F(num , 1 , n)<<endl;
	return 0;
} 

动态规划

改动态规划的方法就是加上记忆化,
因为是从一个区间里面抽牌 需要表示L到R的最大值
所以用一个二维数组存储DP

Dp算法基本求解步骤:

  1. 划分子问题确定状态 dp[L][R]
  2. 状态转移方程
  3. 寻找边界条件 L==R?0:max()

Code

int windp(vector<int> arr){
    
    
	int len= arr.size();
	if(len==0 || arr==NULL)return 0; 

	int f[len][len],s[len][len]; //先手的图和后手的图
	for(int i=0;i<len;i++){
    
    
		f[i][i] = arr[i]; //对角线
	}

	for(int col=1; col<len; col++){
    
    
	    //对角线出发位置
		int L = 0;
		int R = col;
		while(L<len && R<len){
    
    
			f[L][R] = max( arr[L]+s[L+1][R] , arr[R]+s[L][R-1] ); //先手等于自己这次抽的加上 后手抽剩的最小值(从另一个图中更新)
			s[L][R] = min( f[L+1][R] , f[L][R-1] );
			L++;
			R++;
		}
	} 
	return max(f[0][len-1],s[0][len-1]);
}

S表依赖自己下和左的值 谁小 谁是
F表依赖自己arr上的和S表中对应的点

注解:上面的图为F 下面的图是S
F更新时 依赖arr[ ]和后手S选剩下的值(最小值)
当F选L之后 S返回的是[L+1,R] 此区间在S图中的表示为X点的下面
当F选R之后 S返回的是[L,R-1] 此区间在S图中的表示为X点的左面

S的变化很好理解 选完剩下小的 填进S里给F用
在这里插入图片描述


思路:左程云

猜你喜欢

转载自blog.csdn.net/zhimeng_LQ/article/details/109482016