最后更新
这个题做得也不好,dp[n]尝试写了几下,不太对。
应该是类似于gem theory的题。
当只有1个硬币剩下的时候直接拿走,不BB。
剩俩的时候也都拿了。。
dp[n]表示剩下多少个硬币。
轮到我们拿第i个硬币的时候,有2种情况:
- 我们拿这个 i 。
-
- 对手选择拿1个,就是第i+1个,然后第i+ 2轮到我们,我们再决定。
-
- 对手选择拿2个,i+1和i+2, 然后i+3轮到我们。
- 我们拿这个i,再拿下一个i+1。
-
- 对手选择拿1个,就是第i+2, 然后i+3轮到我们。
-
- 对手选择拿2个,i+2, i+3,然后i+4该我们选择了。
当前怎么选取决于拿1个得的多还是拿2个得的多:
dp[i] = Math.max(拿1个, 拿2个)
但是注意中间对手拿的时候,他也要选择对他有利的,所以再轮到我们的时候,我们就拿得相对小的那个选择。
拿1个 = 当前的1个硬币 + Math.min(第i+2轮到我们,第i+3轮到我们)
拿2个 = 当前的2个硬币 + Math.min(第i+3轮到我们, 第i+4轮到我们)
思路很绕,是一个极小极大的思路,minimax。
另一个地方是并没有急于计算对手能拿多少而减去他的决定,而是尝试在他的选择下直接获取我们的硬币,最后看看拿的数量过不过半。
一头雾水。。。
public class Solution {
public boolean firstWillWin(int[] values) {
if (values.length <= 2) return true;
// max profit when there are n coins left
int[] dp = new int[values.length + 1];
Arrays.fill(dp, -1);
int total = values.length;
dp[0] = 0;
// only 1 left, we get that one
dp[1] = values[total-1];
// only 2 left, we grab those 2 fucking coins
dp[2] = values[total-2] + values[total-1];
// wat will we get when there are n left..
dp[total] = nthProfit(total, dp, values);
int sum = 0;
for (int i : values) sum += i;
return dp[total] > sum/2;
}
public int nthProfit(int leftOver, int[] dp, int[] values) {
if (leftOver <= 0) return 0;
if (dp[leftOver] != -1) return dp[leftOver];
int total = values.length;
int pickOne = values[total - leftOver] +
Math.min(nthProfit(leftOver-2, dp, values),
nthProfit(leftOver-3, dp, values));
int pickTwo = values[total - leftOver] + values[total - leftOver + 1] +
Math.min(nthProfit(leftOver-3, dp, values),
nthProfit(leftOver-4, dp, values));
dp[leftOver] = Math.max(pickOne, pickTwo);
return dp[leftOver];
}
}