了解寻找最佳策略的解决方案,包括挑选金罐

时间:2014-03-05 10:39:05

标签: algorithm

我无法理解this question on CareerCup解决方案背后的原因。

  

黄金游戏:两名玩家A& B.安排了金罐   在一条线上,每个都包含一些金币(玩家可以看到如何   每个金罐里都有很多硬币 - 完美的信息。他们得到   交替转弯,玩家可以从其中一个中选择一个底池   线的末端。获胜者是具有更高数字的玩家   最后的硬币。目标是“最大化”数量   A收集的硬币,假设B也是最佳的。 A开始了   游戏。

     

我们的想法是找到一个让A win知道的最佳策略   B也是最佳的。你会怎么做?

     

最后我被要求编写这个策略!

这是Google采访中的一个问题。

建议的解决方案是:

function max_coin( int *coin, int start, int end ):
    if start > end:
        return 0

    // I DON'T UNDERSTAND THESE NEXT TWO LINES
    int a = coin[start] + min(max_coin(coin, start+2, end), max_coin(coin, start+1, end-1))
    int b = coin[end] + min(max_coin(coin, start+1,end-1), max_coin(coin, start, end-2))

    return max(a,b)

我不明白有两个具体的部分:

  1. 在第一行中为什么我们使用范围[start + 2,end]和[start + 1,end-1]?它总是留下一个硬币罐。不应该是[开始+ 1,结束]因为我们把起始硬币罐拿出来了吗?
  2. 在第一行中,为什么我们采用两个结果中的最小值而不是最大值?
  3. 因为我很困惑为什么两条线路最小化以及为什么我们选择这些特定范围,我不确定ab实际代表什么?

5 个答案:

答案 0 :(得分:13)

首先ab分别代表start(分别为end)的最大增益。

所以让我们解释一下这句话:

int a = coin[start] + min(max_coin(coin, start+2, end), max_coin(coin, start+1, end-1))

如果我玩start,我会立即获得coin[start]。另一位玩家现在必须在start+1end之间进行游戏。他发挥最大化他的收益。然而,由于硬币的数量是固定的,这相当于最小化了我的。注意

  • 如果他玩start+1,我将获得max_coin(coin, start+2, end)
  • 如果他玩end生病max_coin(coin, start+1, end-1)

由于他试图最小化我的收益,我将获得这两者中的最小值。

同样的理由适用于我玩end的其他行。

注意:这是一个糟糕的递归实现。首先,max_coin(coin, start+1, end-1)计算两次。即使你解决了这个问题,你最终也会计算出大量的时间。这与您尝试使用递归计算Fibonacci数时会发生的情况非常相似。使用memoization或动态编程会更好。

答案 1 :(得分:6)

ab分别表示通过选择起始底池或结束底池可以获得的最大A

我们实际上是在尝试最大化A-B,但是从B = TotalGold - A开始,我们试图最大化2A - TotalGold,并且由于TotalGold是常数,我们正在尝试最大化2A,与A相同,因此我们完全忽略B选择的值,只使用A

递归调用中的更新参数也包括B选择 - 所以coin[start]代表A选择开始,然后B从开始选择下一个,所以它是start+2。对于下一个电话,B从最后选择,因此它是start+1end-1。其余部分也是如此。

我们正在使用min,因为B会尝试最大化自己的利润,因此会选择最小化A利润的选择。

但实际上我会说这个解决方案缺乏一点意义,它只返回一个值,而不是“最优策略”,在我看来,这将是一系列动作。并且它也没有考虑到A无法获胜的可能性,在这种情况下,人们可能想要输出一条消息,表明这是不可能的,但这确实需要与采访者澄清。

答案 2 :(得分:2)

让我以相反的顺序回答你的观点,不知怎的,它似乎更有意义。

3 - a和b代表第一位玩家在分别选择第一个或最后一个底池时将获得的硬币数量

2 - 我们采取最低限度因为它是第二个玩家的选择 - 他/她将采取行动以最小化第一个玩家获得的硬币数量

1 - 第一行显示场景 - 如果第一个玩家已经拿到第一个底池,那么第二个玩家会做什么?如果他/她再次拿到第一个底池,它将离开(开始+ 2,结束)。如果他/她拿到最后一个底池,它将离开(开始+ 1,结束-1)

答案 3 :(得分:1)

假设轮到你获得的是x,并且在所有后续轮次中得到的是y。这两个值代表x+y,其中a假设您从前面获取下一个底池(x=coin[start]),b假设您从下一个底池(x=coin[end])开始背部。

现在你如何计算y

在您选择之后,对手将使用相同的最佳策略(因此递归调用)以最大化其利润,并且您将获得转弯时较小的利润。这就是为什么你的y=min(best_strategy_front(), best_strategy_end()) - 你的价值是剩下的两个选择中较小的一个,因为对手会占据更大的位置。

索引只是表明你选择后剩下的序列减去正面和背面的一个底池。

答案 4 :(得分:0)

我也要付出一分钱。我已经详细解释了步骤。

public class Problem08 {
  static int dp[][];

  public static int optimalGameStrategy(int arr[], int i, int j) {
    //If one single element then choose that.
    if(i == j) return arr[i];

    //If only two elements then choose the max.
    if (i + 1 == j ) return Math.max(arr[i], arr[j]);

    //If the result is already computed, then return that.
    if(dp[i][j] != -1) return dp[i][j];

    /**
     * If I choose i, then the array length will shrink to i+1 to j.
     * The next move is of the opponent. And whatever he choose, I would want the result to be
     * minimum. If he choose j, then array will shrink to i+1, j-1. But if also choose i then
     * array will shrink to i+2,j. Whatever he choose, I want the result to be min, hence I take
     * the minimum of his two choices.
     *
     * Similarly for a case, when I choose j.
     *
     * I will eventually take the maximum of both of my case. :)
     */
    int iChooseI = arr[i] + Math.min(optimalGameStrategy(arr, i+1, j-1),
        optimalGameStrategy(arr, i+2, j));
    int iChooseJ = arr[j] + Math.min(optimalGameStrategy(arr, i+1, j-1),
        optimalGameStrategy(arr, i, j-2));
    int res = Math.max(iChooseI, iChooseJ );
    dp[i][j] = res;
    return res;
  }


  public static void main(String[] args) {
    int[] arr = new int[]{5,3,7,10};
    dp = new int[arr.length][arr.length];
    for(int i=0; i < arr.length; i++) {
      for(int j=0; j < arr.length; j++) {
        dp[i][j] = -1;
      }
    }
    System.out.println( " Nas: " + optimalGameStrategy(arr, 0, arr.length-1));
  }
}