动态规划02左右取牌返回最大点数

本文探讨了一种策略游戏中的算法设计,通过递归、傻缓存法(动态规划)优化玩家A和B的纸牌取舍决策,以求最大化得分。两种方法对比,揭示了动态规划在减少重复计算上的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

给定一个整型数组arr,代表数值不同的纸牌排成一条线

玩家A和玩家B依次拿走每张纸牌

规定玩家A先拿,玩家B后拿

但是每个玩家每次只能拿走最左或最右的纸牌

玩家A和玩家B都绝顶聪明

请返回最后获胜者的分数

暴力递归

首先设置一个尝试函数processf,参数为arr数组,L表示左边界,R表示右边界,返回值为int表示取到数的和。

先来看basecase:

当只有一张牌的时候,即L==R的时候,拿一张牌,拿到的就是最后这张牌。

普通情况

拿左边的牌,获得值为arr[L],但是对方接下来要拿牌 拿的就是[L+1,R]这两张牌中的一张

拿右边的牌,获得值为arr[L],但是对方接下来要拿牌 拿的就是[L,R-1]这两张牌中的一张

那如何选择呢,再写一个后手函数,表示当前牌组,先等对方拿完以后,自己才能拿。

那么 拿左边的牌获得的值就是arr[L]+processg(arr,L+1,R)

拿右边的牌获得的值就是arr[R]+processg(arr,L,R-1),所以取这两种情况中的最大值

分析后手函数processg

basecase:

当只有一张牌的时候,等对方先拿,然后没牌了,只能返回0

普通情况:

对方拿了左侧的牌 剩下的是L+1,R的牌

对方拿了右侧的牌 剩下的是L,R-1的牌

这两种情况,对方会让你拿到的是最小的,所以两者取min

代码

int processg(vector<int>&, int, int);
int processf(vector<int>& arr, int L, int R) {
	if (L == R) {
		return arr[L];
	}
	int p1 = arr[L] + processg(arr, L + 1, R);
	int p2 = arr[R] + processg(arr, L, R - 1);
	return max(p1, p2);
}
int processg(vector<int>& arr, int L, int R) {
	if (L == R) {
		return 0;
	}
	int p1 =  processf(arr, L + 1, R);
	int p2 =  processf(arr, L, R - 1);
	return min(p1, p2);
}
int way1(vector<int>&arr) {
	int L = 0;
	int R = arr.size()-1;
	int first = processf(arr, L, R);
	int second = processg(arr, L, R);
	return max(first, second);
}

傻缓存法

暴力递归的方法存在重复的子问题,所以使用傻缓存法,将计算过的子问题记录在表中。

由于有两个递归函数,那么就有两张表。先手函数和后手函数各有一张。

可变参数为左边界L和右边界R,范围都是0到N-1,所以建两张N*N的表。

int processg1(vector<int>&, int, int, vector<vector<int>>&, vector<vector<int>>&);
int processf1(vector<int>& arr, int L, int R, vector<vector<int>>& dpf, vector<vector<int>>& dpg) {
	if (dpf[L][R] != -1) {
		return dpf[L][R];
	}
	int ans = 0;
	if (L == R) {
		ans = arr[L];
	}
	else {
		int p1 = arr[L] + processg1(arr, L + 1, R, dpf, dpg);
		int p2 = arr[R] + processg1(arr, L, R - 1, dpf, dpg);
		ans = max(p1, p2);
	}

	dpf[L][R] = ans;
	return ans;
}
int processg1(vector<int>& arr, int L, int R, vector<vector<int>>& dpf, vector<vector<int>>& dpg) {
	if (dpg[L][R] != -1) {
		return dpg[L][R];
	}
	int ans = 0;
	if (L == R) {
		ans= 0;
	}
	else {
		int p1 = processf1(arr, L + 1, R, dpf, dpg);
		int p2 = processf1(arr, L, R - 1, dpf, dpg);
		ans = min(p1, p2);
	}

	dpg[L][R] = ans;
	return ans;
}
int way2(vector<int>& arr) {
	int L = 0;
	int R = arr.size() - 1;
	int N = arr.size();
	vector<vector<int>>dpf(N, vector<int>(N, -1));
	vector<vector<int>>dpg(N, vector<int>(N, -1));
	int first = processf1(arr, L, R, dpf, dpg);
	int second = processg1(arr, L, R, dpf, dpg);
	return max(first, second);
}

值得注意的一点是在原递归函数中可以一直if判断的单句,在傻缓存的递归函数中必须用else 一次性处理完成。

动态规划

分析位置表依赖

dpf表中的basecase:当L==R的时候,即对角线上的值为arr[L]。

dpg表中的basecase:当L==R的时候,即对角线上的值为0.

同时,右边界不可能到达左边界的左边,所以表的下三角是无效的

分析普遍位置依赖:

依赖对应位置的下和左,所以先从对角线上方计算即可。

int way3(vector<int>& arr) {
	int N = arr.size();
	vector<vector<int>>dpf(N, vector<int>(N, 0));
	vector<vector<int>>dpg(N, vector<int>(N, 0));
	for (int i = 0; i < N; i++) {
		dpf[i][i] = arr[i];
	}
	for (int startcol = 1; startcol < N; startcol++) {
		int L = 0;
		int R = startcol;
		while (R < N) {
			dpf[L][R] = max(arr[L] + dpg[L + 1][R], arr[R] + dpg[L][R - 1]);
			dpg[L][R] = min(dpf[L + 1][R], dpf[L][R - 1]);
			L++;
			R++;
		}
	}
	return max(dpf[0][N - 1], dpg[0][N - 1]);

}

code技巧-填对角线的方法表格大小为N*N,沿对角线向上填

for(int startcol=1;startcol<N;startcol++){
    int L=0;
    int R=startcol;
    while(R<N){
        //代码
        L++;
        R++
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值