0

有一个 2 人游戏,你有一个数字序列,例如:2 -6 12. 可以说它们写在卡片上。

游戏需要时间轮流。每个回合玩家有义务从序列的开始或结束处拿走一张牌(不可跳过)。取完最后一张牌后游戏结束。目的是以尽可能高的正分数结束游戏(分数是玩家所拿卡上所有数字的总和)。我们也知道两个玩家都使用最优策略(最大化他们的收益)。我们必须说他们最终会达到什么分数。

知道最优策略是什么样子的吗?

到目前为止我的研究:

1-3 cards is trivial
{a}, no choice take a;
{a,b} take max(a,b) reduces to problem {a}
{a,b,c} take max(a,c) reduces to problem {a,b}
4 cards : {a,b,c,d}
if (a + max(c, min(b,d)) > d + max(b, min(a,c)))
    take a;
else
    take d;

如果我决定拿a,我的对手拿 max(b,d) 就像 3 张牌策略说的那样,所以我要做的就是拿最大的c(在对手转牌时是“安全的”),从b,中取较小的d牌,因为对手会需要更大的一个。双胞胎的情况d。但我不知道如何扩展(如果可能的话)n-cards 案例。

有什么线索吗?

4

4 回答 4

1
//globals
int[N] cards;
int[N][N] v;  //initialized to 0
int[N][N] sum; // precomputed such that sum(i,j)=cards[i]+...+cards[j]

void updateValue(int i,int j){
    int left=cards[i]+sum(i+1,j)-v(i+1,j);
    int right=cards[j]+sum(i,j-1)-v(i,j-1);
    v[i,j]=max(left,right);
}

void do(){
    for (int d=1;d<N;d++)
        for (int i=0;i<N-d;i++)
            updateValue(i,i+d);  
}

答案将在值 [0,N-1] 中

可以通过注意以下几点来提高内存使用率:如果我们将 v 视为一个大方桌,我们会填充它的上三角形一半。对于外循环中的每个 d 值,我们填充从 [0,d] 到 [Nd,N-1]的对角线。现在请注意,在填充这条对角线时,我们只使用之前最后一条对角线的值,因此我们实际上不需要将所有先前的值保存在内存中。

于 2013-04-06T04:58:30.610 回答
0

乍一看让我想起了 Knackpack 问题,但后来我意识到这是一个递归问题。你熟悉 OCaml 吗?考虑这个问题的方法是根据“一组功能”。

需要从基本案例入手,定义基本功能:

e.g. f1(a) -> a
     f2(x, y ) -> max(x,y)
     f3(x, y, z) -> max(f(x,y),z)

然后您需要定义更复杂的情况,如下所示:

if (a + max(c, min(b,d)) > d + max(b, min(a,c)))
    take a;
else
    take d;

这看起来像一个 4 输入函数,您在其中使用先前定义的 max 和 min。像这样的东西:

f2 (a, b,c, d) -> if (a + max(c, min(b,d)) > d + max(b, min(a,c))) then true, else false
f3(a, b,c, d) -> if(f2(a,b,c,d) then a else d

如果需要,您需要定义“基本”函数 f2、f3 和其他函数,然后将输入值替换为其他函数的输出。

我知道这不是一个解决方案,但希望是一个足够好的提示,可以开始以递归方式进行推理。

于 2013-04-03T01:25:28.593 回答
0

我认为这样的事情会起作用:

int[] cards;

int low = 0;
int high = cards.length - 1;

int bestScore(int low, int high)
{
  if (low > high)
    return 0;

  // our best score is our immediate score minus the opponents best response
  int lowScore = cards[low] - bestScore(low + 1, high);
  int highScore = cards[high] - bestScore(low, high - 1);

  if (lowScore >= highScore)
    return lowScore;
  else
    return highScore;
}

int bestMove(int low, int high)
{
  int lowScore = cards[low] - bestScore(low + 1, high);
  int highScore = cards[high] - bestScore(low, high - 1);

  if (lowScore >= highScore)
    return low;
  else
    return high;
}
于 2013-04-03T01:31:40.857 回答
0

如果序列的长度是偶数,则回答:

第一个玩家总是有一个不松懈的策略。关键的观察是,第一个玩家可以收集所有偶数位的数字,如果他愿意,他可以收集所有奇数位的数字。所以他只需要事先检查哪个总和更大:

如果序列是 {x_1, x_2, ...,x_n} 其中 n=2k

计算:A=x_1+x_3_...x_2k-1 和 B=x_2+x_4+...+x_2k

如果 A>=B,首先选择 x_2k,无论对手做什么,第一个玩家总是可以选择 x_2i 获得一些 i。如果 A< B,从选择 x_1 开始,无论对手做什么,第一个玩家总是可以选择 x_2i+1 来获得一些 i。

于 2013-04-03T20:35:35.287 回答