第三季第一节课——题目3


有一排正数,玩家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中的一个,…


Win1思路:

用函数f和s分别表示先发和后发的情况下,在所剩区间选择剩余数字能得到的最大值。

返回先发者和后发者两者中的最大分数

 

Win2思路:

Win2是在win1的基础上做的优化,把f和s的值分别用一张二维表来描述,f[I,j]表示剩余i到j区间上的牌时,先选能取得的最大收益。

观察win1函数可以发现,其最终要求的是f(arr,0,N-1),这个值对应于f表中的最右上角的位置,而根据f函数又可以发现,该位置依赖s表中对应位置的下一个位置和左边的一个位置。

同理,s表中某位置的值也依赖于f表中相应位置的下一个位置和左边位置的值。又已知f表中对角线的位置为1,s表中对角线位置上的元素为0,就这样根据这两个表的值,往表的上面推,可以推出右上角位置上的元素。

 

Win3思路:

Win3又是在Win1的基础上进一步优化,p函数可看作我先发,在剩余区间i到j上的最大收益。

当剩余一个数时,则收益即为该值

当剩余两个数时,肯定选大的:

(1)当我拿最左边的数,即arr[i]:则他可能拿剩下的最左边arr[i+1]和最右边的数arr[j];当对方拿arr[i+1]时,我就只能拿[i+2,j]中的数,当对方拿arr[j]时,我只能拿[i+1,j-1]中的数,但是我拿的肯定是这两个数中的最小值

当我拿最右边的数,即arr[j]:则他可能拿剩下的最左边arr[i]和最右边的数arr[j-1];当对方拿arr[i]时,我就只能拿[i+1,j]中的数,当对方拿arr[j-1]时,我只能拿[i,j-2]中的数,但是我拿的肯定还是这两个数中的最小值

到底我拿最左边还是左右边,哪种收益高就拿哪种


Win4:动态规划

public class Problem_03_CardsInLine {

	public static int win1(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));
	}

	public static int win2(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]);
	}

	public static int win3(int[] arr) {
		if (arr == null || arr.length == 0) {
			return 0;
		}
		int sum = 0;
		for (int i = 0; i < arr.length; i++) {
			sum += arr[i];
		}
		int scores = p(arr, 0, arr.length - 1);
		return Math.max(sum - scores, scores);
	}

	public static int p(int[] arr, int i, int j) {
		if (i == j) {
			return arr[i];
		}
		if (i + 1 == j) {
			return Math.max(arr[i], arr[j]);
		}
		return Math.max(arr[i] + Math.min(p(arr, i + 2, j), p(arr, i + 1, j - 1)),
				arr[j] + Math.min(p(arr, i + 1, j - 1), p(arr, i, j - 2)));
	}

	public static int win4(int[] arr) {
		if (arr == null || arr.length == 0) {
			return 0;
		}
		if (arr.length == 1) {
			return arr[0];
		}
		if (arr.length == 2) {
			return Math.max(arr[0], arr[1]);
		}
		int sum = 0;
		for (int i = 0; i < arr.length; i++) {
			sum += arr[i];
		}
		int[][] dp = new int[arr.length][arr.length];
		for (int i = 0; i < arr.length - 1; i++) {
			dp[i][i] = arr[i];
			dp[i][i + 1] = Math.max(arr[i], arr[i + 1]);
		}
		dp[arr.length - 1][arr.length - 1] = arr[arr.length - 1];
		for (int k = 2; k < arr.length; k++) {
			for (int j = k; j < arr.length; j++) {
				int i = j - k;
				dp[i][j] = Math.max(arr[i] + Math.min(dp[i + 2][j], dp[i + 1][j - 1]),
						arr[j] + Math.min(dp[i + 1][j - 1], dp[i][j - 2]));
			}
		}
		return Math.max(dp[0][arr.length - 1], sum - dp[0][arr.length - 1]);
	}

	public static int[] generateRondomArray() {
		int[] res = new int[(int) (Math.random() * 20) + 1];
		for (int i = 0; i < res.length; i++) {
			res[i] = (int) (Math.random() * 20) + 1;
		}
		return res;
	}

	public static void main(String[] args) {
		int testTime = 50000;
		boolean err = false;
		for (int i = 0; i < testTime; i++) {
			int[] arr = generateRondomArray();
			int r1 = win1(arr);
			int r2 = win2(arr);
			int r3 = win3(arr);
			int r4 = win4(arr);
			if (r1 != r2 || r1 != r3 || r1 != r4) {
				err = true;
			}
		}
		if (err) {
			System.out.println("2333333333");
		} else {
			System.out.println("6666666666");
		}
	}

}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值