从数组两头抽取元素游戏

Posted 贾斯彼迭

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从数组两头抽取元素游戏相关的知识,希望对你有一定的参考价值。

给定一个数组,玩家A,B每次从数组头或尾取数,且只能从头尾取。假定A,B都绝顶聪明,均采取最优策略,判断A先手的情况下,A是否能够获胜。

 

分析:  f(i, j) 表示 在arr[i~j]中A 先手时能够获得的最大分数,s(i, j) 表示 A后手时能够获得的最大分数。

首先分析f(i, j)。 A可先取arr[i], 取完后剩余arr[i+1, j]。此时相当于A后手在[i+1, j]的情况了。

也可先取arr[j], 取完后剩余arr[i, j - 1]。 此时相当于A后手在[i, j -1]的情况了。

则 f(i, j) = max{arr[i] + s(i+1, j), arr[j] + s(i, j-1)}

再分析s(i, j)。B可先取arr[i] 或 arr[j] 。取完后相当于A先手的情况了。只是在这种情况下,B会留下最差解。

s(i, j) = min{arr[i] + f(i+1, j), arr[j] + f(i, j-1)};

故可生成两个二维矩阵。代码如下:

边界条件:

f[j][j] = arr[j]; 因为只有一个数,先手必取这个数。
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];
		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 f[0][arr.length - 1], s[0][arr.length - 1];
	}

  

上述方法时间复杂度O(N2),用到了两个二维数组。可以在空间复杂度上稍微优化一下,只使用1个二维数组。

用dp[i][j]表示在arr[i~j]中A先手的情况能取得的最大分数。则A的这一次先手与下一次先手有4种可能:

1. A  取 arr[i],      B 取 arr[j]   ,  剩下[i+1, j-1]

2. A  取 arr[i],    B 取arr[i+1],  剩下[i+2, j]

3. A  取 arr[j],    B 取arr[i],      剩下[i+1, j-1]

4. A  取arr[j] ,    B 取arr[j-1],   剩下[i, j-2]

前两种情况取最小情况,后两种情况取最小情况,然后取最大值。其实就是综合之前的情况。

边界条件:

i == j时,就是取arr[i]; 同时,此种情况必须保证j >= i +2, 即至少有3个数。代码如下:

 

public  static int win1(int[] arr){
		if(arr == null || arr.length == 0)
			return 0;
		
		int len = arr.length;
		int[][] dp = new int[len][len];
		int part1, part2, part3, part4;
		
		for(int i = 0; i < len; i++)
			dp[i][i] = arr[i];
		
		for(int i = len - 1; i >= 0; i--)
			for(int j = i+1; j < len; j++){
				if( j >= i+2 ){
					part1 = ( i <= len -3 ? (arr[i] + dp[i+2][j]) : 0 );
					part2 =  ((i <= len -2 && j >= 1) ? (arr[i] +dp[i+1][j]) : 0);
					part3 = (j >= 2 ? (arr[j] + dp[i][j-2]) : 0);
					part4 =  ((j >= 1 && i <= len-2) ? (arr[j] + dp[i+1][j-1]) : 0);
				
					dp[i][j] = Math.max(Math.min(part1, part2), Math.min(part3, part4));
				}else
					dp[i][j] = Math.max(arr[i], arr[j]);
			}
		
		return dp[0][len-1];
	}

  

以上是关于从数组两头抽取元素游戏的主要内容,如果未能解决你的问题,请参考以下文章

从数组中随机抽取一个值,(别人问我,我自己想到的一个方法)

thymeleaf公共页面元素抽取

如何从 C# 代码在 GameObjects 数组中添加游戏对象?

从片段调用 Google Play 游戏服务

LeetCode810. 黑板异或游戏/455. 分发饼干/剑指Offer 53 - I. 在排序数组中查找数字 I/53 - II. 0~n-1中缺失的数字/54. 二叉搜索树的第k大节点(代码片段

数组,避免拉取 Duplicates,简单代码,C++