您的位置:首页 > 其它

LintCode 395: First Will Win 2

2017-02-26 14:54 211 查看

LintCode 395: First Will Win 2

题目描述

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

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

样例

给定数组
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]\]

(还剩下两个硬币当然要全部拿完了)

\[dp[n - 3] = values[n - 2] + values[n - 3]\] (还剩下三个硬币当然要拿两个了)

对于一般情况:

\[
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]\) 的情况,如此时拿两个硬笔,那么剩下的两个肯定对手肯定会全都拿了,不会再去权衡博弈了。

代码

// 硬币排成线 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: