トピック住所:
https://leetcode.com/problems/predict-the-winner/
非負整数配列を考えます。2つのゲームの設計、アレイからのような二人の人物AとB、Aは、左右のエンドポイントを取り、その後、酢酸を取り、最後に巻き取りを指すことができます。Aは、合計スコアに等しい場合にはBより高く、それはそうでなければ偽を返し、trueを返します。上部左側。
アイデアはこれです:2つだけの数字ならば、結論は非常に簡単にすることができます。私たちは小さな規模の問題のために大きな問題を置くように、二つ以上の数字ならば、列挙、左上手側およびこれら2例右の端を取ります。しかし、難しさはそれは、ちょうど我々はポイント差に勝った人の考えることができるので、誰が勝つかの小規模な問題は、十分ではない、大規模な問題を誰が勝つか知っていることを知っています。私たちは知っている場合 、問題サイズ、上側のハンド側フリップ点の分散比 、そして、スケールのために ちょうどフリップ側はマイナス規模、上側の手側であるよりも、当事者がポイント差の割合を取るために得る問題、 問題上部左側及び相違点のフリップ側(この場合は手を交換されました)。最後に、ちょうどかどうか非負にポイント差を返します。
方法1:メモリのDFS +。
public class Solution {
public boolean PredictTheWinner(int[] nums) {
// 如果只有一个数字,返回true,先手必胜
if (nums.length == 1) {
return true;
}
// f[i][j]记录对于数组nums[i,...,j],先手方与后手方的分差
// 做记忆化,防止重复搜索
int[][] f = new int[nums.length][nums.length];
return dfs(nums, 0, nums.length - 1, f) >= 0;
}
private int dfs(int[] nums, int i, int j, int[][] f) {
// 递归出口
if (j - i == 1) {
f[i][j] = Math.abs(nums[i] - nums[j]);
return f[i][j];
}
// 如果f里有记忆,直接调取记忆;否则接着暴搜
// score1是先手选了数组左端的情况下,后手与先手的分差
int score1 = f[i + 1][j] != 0 ? f[i + 1][j] : dfs(nums, i + 1, j, f);
// score2是先手选了数组右端的情况下,后手与先手的分差
int score2 = f[i][j - 1] != 0 ? f[i][j - 1] : dfs(nums, i, j - 1, f);
// 返回之前做一下记忆
f[i][j] = Math.max(nums[i] - score1, nums[j] - score2);
return f[i][j];
}
}
時間と空間の複雑さ 。嵐はその2次元マトリックスを見つけたときの方法のメモリがどうなる時の複雑さの分析は、検索に各リーフノードの背中をそれを見ることができないので、これは、各エントリの2次元マトリックスは最大で1回で発見です。
方法2:動的プログラミング。検索の本質との思い出では同じです。唯一の更新順序が不明を更新するために知らなければならないことに注意。
public class Solution {
public boolean PredictTheWinner(int[] nums) {
if (nums.length == 1) {
return true;
}
int[][] dp = new int[nums.length - 1][nums.length];
for (int j = 1; j < nums.length; j++) {
for (int i = j - 1; i >= 0; i--) {
if (j - i == 1) {
dp[i][j] = Math.abs(nums[i] - nums[j]);
} else {
dp[i][j] = Math.max(nums[i] - dp[i + 1][j], nums[j] - dp[i][j - 1]);
}
}
}
return dp[0][nums.length - 1] >= 0;
}
}
同じの時間と空間の複雑さ。
次に、空間複雑さを最適化することを検討し、直感的なアイデアは、一次元アレイ、アレイ後で再利用上書き未使用の値です。私たちは、いくつかの時点で私たちが行列を更新することを想像します ライン 列には、この数は、第1にのみ依存します ライン 列、第 ライン 列の数、我々は、具体的平均、行をスクロール更新することができますので、我々は行を表す、1次元配列を初期化するには、その後、新しい行は、更新に更新タイムラインを古いラインを使用します 左から右に更新する必要があります。スペースの最適化は、キーシーケンスを更新することを決定し、かつ、既に不明の更新値の値を計算し使用することが確実である場合には、唯一の方法は、正しい答えを確保します。コードは以下の通りであります:
public class Solution {
public boolean PredictTheWinner(int[] nums) {
if (nums.length == 1) {
return true;
}
int[] dp = new int[nums.length];
for (int i = nums.length - 2; i >= 0; i--) {
for (int j = i + 1; j < nums.length; j++) {
if (j - i == 1) {
dp[j] = Math.abs(nums[i] - nums[j]);
} else {
dp[j] = Math.max(nums[i] - dp[j], nums[j] - dp[j - 1]);
}
}
}
return dp[nums.length - 1] >= 0;
}
}
同じ時間の複雑さ、スペース 。