收集点游戏

时间:2013-04-03 01:09:01

标签: algorithm optimization

有一个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取得最大值(这是“安全的”在对手转身时),从bd卡开始变小,因为对手会占据更大的牌。 d的双重情况。但我不知道如何扩展(如果可能的话)n卡的情况。

任何线索?

4 个答案:

答案 0 :(得分: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]到[N-d,N-1]的对角。现在请注意,在填充此对角线时,我们只使用之前对角线的值,因此我们并不需要将所有先前的值保存在内存中。

答案 1 :(得分: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和其他函数,然后将输入值替换为其他函数的输出。

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

答案 2 :(得分: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;
}

答案 3 :(得分: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&gt; = B,首先选择x_2k,无论对手做什么,第一个玩家总是可以为某些i选择x_2i。如果A&lt; B,从选择x_1开始,无论对手做什么,第一个玩家总能为某些人选择x_2i + 1.