Skip to content

Files

Latest commit

 

History

History

0486.Predict the Winner

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
Feb 21, 2024
Feb 21, 2024
Jan 13, 2024
Jan 13, 2024
Jan 13, 2024
Jan 13, 2024
Nov 9, 2023
Jan 13, 2024
Jan 13, 2024
Jan 13, 2024
Jan 13, 2024
Jan 13, 2024
Jan 13, 2024

English Version

题目描述

给你一个整数数组 nums 。玩家 1 和玩家 2 基于这个数组设计了一个游戏。

玩家 1 和玩家 2 轮流进行自己的回合,玩家 1 先手。开始时,两个玩家的初始分值都是 0 。每一回合,玩家从数组的任意一端取一个数字(即,nums[0]nums[nums.length - 1]),取到的数字将会从数组中移除(数组长度减 1 )。玩家选中的数字将会加到他的得分上。当数组中没有剩余数字可取时,游戏结束。

如果玩家 1 能成为赢家,返回 true 。如果两个玩家得分相等,同样认为玩家 1 是游戏的赢家,也返回 true 。你可以假设每个玩家的玩法都会使他的分数最大化。

 

示例 1:

输入:nums = [1,5,2]
输出:false
解释:一开始,玩家 1 可以从 1 和 2 中进行选择。
如果他选择 2(或者 1 ),那么玩家 2 可以从 1(或者 2 )和 5 中进行选择。如果玩家 2 选择了 5 ,那么玩家 1 则只剩下 1(或者 2 )可选。 
所以,玩家 1 的最终分数为 1 + 2 = 3,而玩家 2 为 5 。
因此,玩家 1 永远不会成为赢家,返回 false 。

示例 2:

输入:nums = [1,5,233,7]
输出:true
解释:玩家 1 一开始选择 1 。然后玩家 2 必须从 5 和 7 中进行选择。无论玩家 2 选择了哪个,玩家 1 都可以选择 233 。
最终,玩家 1(234 分)比玩家 2(12 分)获得更多的分数,所以返回 true,表示玩家 1 可以成为赢家。

 

提示:

  • 1 <= nums.length <= 20
  • 0 <= nums[i] <= 107

解法

方法一:记忆化搜索

我们设计一个函数 d f s ( i , j ) ,表示从第 i 个数到第 j 个数,当前玩家与另一个玩家的得分之差的最大值。那么答案就是 d f s ( 0 , n 1 ) > 0

函数 d f s ( i , j ) 的计算方法如下:

  • 如果 i > j ,说明当前没有数字了,所以当前玩家没有分数可以拿,差值为 0 ,即 d f s ( i , j ) = 0
  • 否则,当前玩家有两种选择,如果选择第 i 个数,那么当前玩家与另一个玩家的得分之差为 n u m s [ i ] d f s ( i + 1 , j ) ;如果选择第 j 个数,那么当前玩家与另一个玩家的得分之差为 n u m s [ j ] d f s ( i , j 1 ) 。当前玩家会选择两种情况中差值较大的情况,也就是说 d f s ( i , j ) = max ( n u m s [ i ] d f s ( i + 1 , j ) , n u m s [ j ] d f s ( i , j 1 ) )

最后,我们只需要判断 d f s ( 0 , n 1 ) > 0 即可。

为了避免重复计算,我们可以使用记忆化搜索的方法,用一个数组 f 记录所有的 d f s ( i , j ) 的值,当函数再次被调用到时,我们可以直接从 f 中取出答案而不需要重新计算。

时间复杂度 O ( n 2 ) ,空间复杂度 O ( n 2 ) 。其中 n 是数组的长度。

class Solution:
    def PredictTheWinner(self, nums: List[int]) -> bool:
        @cache
        def dfs(i: int, j: int) -> int:
            if i > j:
                return 0
            return max(nums[i] - dfs(i + 1, j), nums[j] - dfs(i, j - 1))

        return dfs(0, len(nums) - 1) >= 0
class Solution {
    private int[] nums;
    private int[][] f;

    public boolean PredictTheWinner(int[] nums) {
        this.nums = nums;
        int n = nums.length;
        f = new int[n][n];
        return dfs(0, n - 1) >= 0;
    }

    private int dfs(int i, int j) {
        if (i > j) {
            return 0;
        }
        if (f[i][j] != 0) {
            return f[i][j];
        }
        return f[i][j] = Math.max(nums[i] - dfs(i + 1, j), nums[j] - dfs(i, j - 1));
    }
}
class Solution {
public:
    bool PredictTheWinner(vector<int>& nums) {
        int n = nums.size();
        int f[n][n];
        memset(f, 0, sizeof(f));
        function<int(int, int)> dfs = [&](int i, int j) -> int {
            if (i > j) {
                return 0;
            }
            if (f[i][j]) {
                return f[i][j];
            }
            return f[i][j] = max(nums[i] - dfs(i + 1, j), nums[j] - dfs(i, j - 1));
        };
        return dfs(0, n - 1) >= 0;
    }
};
func PredictTheWinner(nums []int) bool {
	n := len(nums)
	f := make([][]int, n)
	for i := range f {
		f[i] = make([]int, n)
	}
	var dfs func(i, j int) int
	dfs = func(i, j int) int {
		if i > j {
			return 0
		}
		if f[i][j] == 0 {
			f[i][j] = max(nums[i]-dfs(i+1, j), nums[j]-dfs(i, j-1))
		}
		return f[i][j]
	}
	return dfs(0, n-1) >= 0
}
function PredictTheWinner(nums: number[]): boolean {
    const n = nums.length;
    const f: number[][] = new Array(n).fill(0).map(() => new Array(n).fill(0));
    const dfs = (i: number, j: number): number => {
        if (i > j) {
            return 0;
        }
        if (f[i][j] === 0) {
            f[i][j] = Math.max(nums[i] - dfs(i + 1, j), nums[j] - dfs(i, j - 1));
        }
        return f[i][j];
    };
    return dfs(0, n - 1) >= 0;
}
impl Solution {
    #[allow(dead_code)]
    pub fn predict_the_winner(nums: Vec<i32>) -> bool {
        let n = nums.len();
        let mut dp: Vec<Vec<i32>> = vec![vec![0; n]; n];

        // Initialize the dp vector
        for i in 0..n {
            dp[i][i] = nums[i];
        }

        // Begin the dp process
        for i in (0..n - 1).rev() {
            for j in i + 1..n {
                dp[i][j] = std::cmp::max(
                    // Take i-th num
                    nums[i] - dp[i + 1][j],
                    // Take j-th num
                    nums[j] - dp[i][j - 1]
                );
            }
        }

        dp[0][n - 1] >= 0
    }
}

方法二:动态规划

我们也可以使用动态规划的方法,定义 f [ i ] [ j ] 表示当前玩家在 n u m s [ i . . j ] 这些数字中能够获得的最大得分的差值。那么最后答案就是 f [ 0 ] [ n 1 ] > 0

初始时 f [ i ] [ i ] = n u m s [ i ] ,因为只有一个数,所以当前玩家只能拿取这个数,得分差值为 n u m s [ i ]

考虑 f [ i ] [ j ] ,其中 i < j ,有两种情况:

  • 如果当前玩家拿走了 n u m s [ i ] ,那么剩下的数字为 n u m s [ i + 1. . j ] ,此时轮到另一个玩家进行游戏,所以 f [ i ] [ j ] = n u m s [ i ] f [ i + 1 ] [ j ]
  • 如果当前玩家拿走了 n u m s [ j ] ,那么剩下的数字为 n u m s [ i . . j 1 ] ,此时轮到另一个玩家进行游戏,所以 f [ i ] [ j ] = n u m s [ j ] f [ i ] [ j 1 ]

因此,最终的状态转移方程为 f [ i ] [ j ] = max ( n u m s [ i ] f [ i + 1 ] [ j ] , n u m s [ j ] f [ i ] [ j 1 ] )

最后,我们只需要判断 f [ 0 ] [ n 1 ] > 0 即可。

时间复杂度 O ( n 2 ) ,空间复杂度 O ( n 2 ) 。其中 n 是数组的长度。

相似题目:

class Solution:
    def PredictTheWinner(self, nums: List[int]) -> bool:
        n = len(nums)
        f = [[0] * n for _ in range(n)]
        for i, x in enumerate(nums):
            f[i][i] = x
        for i in range(n - 2, -1, -1):
            for j in range(i + 1, n):
                f[i][j] = max(nums[i] - f[i + 1][j], nums[j] - f[i][j - 1])
        return f[0][n - 1] >= 0
class Solution {
    public boolean PredictTheWinner(int[] nums) {
        int n = nums.length;
        int[][] f = new int[n][n];
        for (int i = 0; i < n; ++i) {
            f[i][i] = nums[i];
        }
        for (int i = n - 2; i >= 0; --i) {
            for (int j = i + 1; j < n; ++j) {
                f[i][j] = Math.max(nums[i] - f[i + 1][j], nums[j] - f[i][j - 1]);
            }
        }
        return f[0][n - 1] >= 0;
    }
}
class Solution {
public:
    bool PredictTheWinner(vector<int>& nums) {
        int n = nums.size();
        int f[n][n];
        memset(f, 0, sizeof(f));
        for (int i = 0; i < n; ++i) {
            f[i][i] = nums[i];
        }
        for (int i = n - 2; ~i; --i) {
            for (int j = i + 1; j < n; ++j) {
                f[i][j] = max(nums[i] - f[i + 1][j], nums[j] - f[i][j - 1]);
            }
        }
        return f[0][n - 1] >= 0;
    }
};
func PredictTheWinner(nums []int) bool {
	n := len(nums)
	f := make([][]int, n)
	for i, x := range nums {
		f[i] = make([]int, n)
		f[i][i] = x
	}
	for i := n - 2; i >= 0; i-- {
		for j := i + 1; j < n; j++ {
			f[i][j] = max(nums[i]-f[i+1][j], nums[j]-f[i][j-1])
		}
	}
	return f[0][n-1] >= 0
}
function PredictTheWinner(nums: number[]): boolean {
    const n = nums.length;
    const f: number[][] = new Array(n).fill(0).map(() => new Array(n).fill(0));
    for (let i = 0; i < n; ++i) {
        f[i][i] = nums[i];
    }
    for (let i = n - 2; i >= 0; --i) {
        for (let j = i + 1; j < n; ++j) {
            f[i][j] = Math.max(nums[i] - f[i + 1][j], nums[j] - f[i][j - 1]);
        }
    }
    return f[0][n - 1] >= 0;
}