LintCode 395: First Will Win 2

LintCode 395: First Will Win 2

题目描述

n 个不同价值的硬币排成一条线。两个参赛者轮流从左边依次拿走 12 个硬币,直到没有硬币为止。计算两个人分别拿到的硬币总价值,价值高的人获胜。

请判定 第一个玩家 是输还是赢?

样例

给定数组 A = [1,2,2], 返回 true.

给定数组 A = [1,2,4], 返回 false.

Sat Feb 26 2017

思路

一般这种博弈的题目都是假设双方足够聪明,能够用最优的策略取得胜利的,看似很麻烦,实际上却简化了问题,因为这样的话每一个状态都确定了。

看起来很像动态规划问题,那么就试试写状态转移方程吧。

设硬币数为 \(n\),令 \(dp[i]\) 为从 \(i\)\(n - 1\) 能获得的最大价值。

显然:

\[dp[n - 1] = values[n - 1] \]

\[dp[n - 2] = values[n - 1] + values[n - 2]$$ <p align="center">(还剩下两个硬币当然要全部拿完了)</p> $$dp[n - 3] = values[n - 2] + values[n - 3]$$ <p align="center">(还剩下三个硬币当然要拿两个了)</p> 对于一般情况: \]

dp[i]\,=\,{\rm max}
\begin{cases}
values[i]\,+\,{\rm min}\{dp[i + 2],\,dp[i + 3]\}, & \text{若本轮拿一个硬币} \\
values[i]\,+\,values[i + 1]\,+\,{\rm min}\{dp[i + 3],\,dp[i + 4]\}, & \text{若本轮拿两个硬币}
\end{cases}

\[ 外层的 ${\rm max}$ 表示“我”要选择一个利于自身的方案:到底是拿一个还是两个。 内层的 ${\rm min}$ 表示对手要选择一个不利于“我”的方案。 最后只要看 $dp[0]$ 是否超过硬币总价值的 $1/2$ 即可。 有一个小细节需要注意一下,就是 $dp[n - 4]$ 的情况,如此时拿两个硬笔,那么剩下的两个肯定对手肯定会全都拿了,不会再去权衡博弈了。 ### **代码** ```C++ // 硬币排成线 II bool firstWillWin(vector<int> &values) { int n = values.size(); if (n <= 2) return true; vector<int> dp(n); dp[n - 1] = values[n - 1]; dp[n - 2] = values[n - 2] + values[n - 1]; dp[n - 3] = values[n - 3] + values[n - 2]; int sum = dp[n - 1] + dp[n - 3]; for (int i = n - 4; i >= 0; --i) { int get_one = values[i] + min(dp[i + 2], dp[i + 3]); int get_two = values[i] + values[i + 1]; if (i != n - 4) get_two += min(dp[i + 3], dp[i + 4]); dp[i] = max(get_one, get_two); sum += values[i]; } return dp[0] > sum / 2; } ```\]

posted @ 2017-02-26 14:54  GenkunAbe  阅读(169)  评论(0编辑  收藏  举报