入门---暴力递归改动态规划

题目:有一排正数,玩家A和玩家B都可以看到。
每位玩家在拿走数字的时候,都只能从最左和最右的数中选择一个。
玩家A先拿,玩家B再拿,两人交替拿走所有的数字,
两人都力争自己拿到的数的总和比对方多。请返回最后获胜者的分数。
例如:
5,2,3,4
玩家A先拿,当前他只能拿走5或者4。
如果玩家A拿走5,那么剩下2,3,4。轮到玩家B,此时玩家B可以选择2或4中的一个,…
如果玩家A拿走4,那么剩下5,2,3。轮到玩家B,此时玩家B可以选择5或3中的一个,…
解法:暴力递归 函数f表示先手拿数字 ,函数S表示后手拿数。

public static int Ways(int[] arr) {
		if (arr == null || arr.length == 0) {
			return 0;
		}
		return Math.max(f(arr, 0, arr.length - 1), s(arr, 0, arr.length - 1));
	}
	//函数f表示先发的情况下,剩余数下标区间为[i,j],能取得的最大收益
	public static int f(int[] arr, int i, int j) {
		//剩余一个数,我先发,则必然我取
		if (i == j) {
			return arr[i];
		}
		//作为先发,我取第i个数的收益+我作为[i+1,j]的后发者的收益  或  我取第j个数的收益+我作为[i,j-1]上的后发者的收益  中的最大值
		return Math.max(arr[i] + s(arr, i + 1, j), 
		arr[j] + s(arr, i, j - 1));
	}
	//s表示后发情况下,在剩余数下标区间为[i,j]中,能取得的最大收益
	public static int s(int[] arr, int i, int j) {
		//作为后发者,剩余一个数的时候肯定不是我取,因此收益为0
		if (i == j) {
			return 0;
		}
		//作为后发者,先发者会让我在[i+1,j]或[i,j-1]上取最小的收益
		return Math.min(f(arr, i + 1, j), f(arr, i, j - 1));
	}

在这里插入图片描述
我们省略函数调用 时的数组,从图中看出在求解过程中,可能导致重复计算f(1,5),效率比较低。计算f(i,j)时,我们依赖s(i+1,j) 和s(i,j-1);同理s(i,j)的计算也依赖与f。我们可以把 f做成一张二维表,同理s做成二维表。
在这里插入图片描述
有暴力递归可知 i==j 就f(i,j)等于arr[i] s(i,j)==0;

public static int Ways20(int[] arr) {
		if (arr == null || arr.length == 0) {
			return 0;
		}
		int[][] f = new int[arr.length][arr.length];
		int[][] s = new int[arr.length][arr.length]; //s会自动初始化为0
		for (int j = 0; j < arr.length; j++) {
			f[j][j] = arr[j];
			for (int i = j - 1; i >= 0; i--) {
				f[i][j] = Math.max(arr[i] + s[i + 1][j], arr[j] + s[i][j - 1]);
				s[i][j] = Math.min(f[i + 1][j], f[i][j - 1]);
			}
		}
		return Math.max(f[0][arr.length - 1], s[0][arr.length - 1]);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值