Algorithm 收集积分游戏

Algorithm 收集积分游戏,algorithm,optimization,Algorithm,Optimization,有一个2人游戏,你有数字序列,例如:2-612。假设它们写在卡片上 比赛轮流进行。每个回合玩家都有义务从一个序列的开始或结束处取一张牌(不跳过)。游戏结束后,最后一张牌已采取。目标是以尽可能高的正分数结束游戏(分数是玩家所拿牌上所有数字的总和)。我们还知道,两个玩家都使用最佳策略(最大化他们的收益)。我们必须说他们最终会达到什么分数 你知道最优策略是什么样子吗 到目前为止,我的研究: 1-3 cards is trivial {a}, no choice take a; {a,b} take m

有一个2人游戏,你有数字序列,例如:
2-612
。假设它们写在卡片上

比赛轮流进行。每个回合玩家都有义务从一个序列的开始或结束处取一张牌(不跳过)。游戏结束后,最后一张牌已采取。目标是以尽可能高的正分数结束游戏(分数是玩家所拿牌上所有数字的总和)。我们还知道,两个玩家都使用最佳策略(最大化他们的收益)。我们必须说他们最终会达到什么分数

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

到目前为止,我的研究:

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
,我的对手就拿最大值(b,d),正如3张牌策略所说的,所以我要做的就是从
c
(在对手回合中是“安全的”)拿最大值,从
b
d
牌中取较小值,因为对手会拿较大的一张。带有
d
的两种情况。但我不知道如何扩展(如果可能的话)n-卡的情况


有什么线索吗?

乍一看我还记得背包问题,但后来我意识到这是一个递归问题。你熟悉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和其他函数,然后用其他函数的输出替换输入值


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

我认为类似的方法会奏效:

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;
}

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

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

如果序列是{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开始,无论对手做什么,第一个玩家总是可以为一些i选择x_2i。如果A
//全局
//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);  
}
int[N]卡; int[N][N]v//已初始化为0 整数[N][N]和;//预先计算,使总和(i,j)=卡片[i]+…+卡片[j] void updateValue(inti,intj){ int left=卡片[i]+和(i+1,j)-v(i+1,j); int right=卡片[j]+总和(i,j-1)-v(i,j-1); v[i,j]=最大值(左,右); } void do(){
对于(intd=1;dyu是错误的。让序列{a,b,c,d}如下{20,-5,20,44}然后a+c=20+20=40>39=44-5=b+d。假设你从拿20开始。然后对手拿了44,你拿了20,对手拿了-5。你的收益是40,op是39。用人脑我可以执行更好的策略:我拿44,op拿20,我拿20,op拿-5。结果:我有64,而对手只有15。而且这并不是我问题的答案r、 因为无论对手做什么都是非常重要的,他都会尝试做一些动作来增加分数(即使他做得很贪婪——假设这是最好的策略,而整个游戏都在第一个玩家身上)。不过,有时取较小的总数更好,因为以后我仍然有机会从敌手对中取较小的数字作为我的第一个post案例#4状态。@kostek我假设“赢家通吃”,也就是说,只要你的分数大于对手的分数,你就赢了。这不是答案(也就是说,它不是n张牌的解算器),但如果有偶数张牌,玩家1总是可以赢。如果有奇数张牌,那么玩家1将获得一张额外的牌(否则最后一张牌将被忽略?),结果将是策略方面的无趣:要么玩家2的策略无关/无效,要么(在奇数张牌的情况下),结果是固定的。老实说,它来自巴西ICPC。对我来说,它似乎是动态规划问题。设置多达10000张卡。如果我理解你的递归算法,它将花费比宇宙生命周期更多的时间。它非常类似于斐波那契递归计算(两个递归调用)。我没有说明什么(无意中省略)是卡号是偶数(如果有帮助的话)是的,对于10000张卡,这种方法将永远需要平方:)不过,您可以将搜索深度限制在合理的范围内,例如20,或者根据已知的运行时间设置一些其他限制,并且可能会得到比人类对手更好的响应。问题是,对于几个测试用例,C卡肯定会在3秒内出现。我希望有人能找到这个主题来帮助我。sorr长时间不活动,但我放弃了获得答案的希望。无论如何,让我们直截了当地说:这一次似乎比前一次好。首先,我有一个抱怨记忆(10^5)^2=10^10,这有点太高了。但我想我们可以解决它。总和可以是每个总和的整数[N]=sum[index-1]+卡片[index]现在我可以很容易地用一个减法1到b得到区间(a,b),1到a得到(a,b),但是不知道如何减少v(值?)你能描述一下吗(一些评论和坚持你以前使用的名字,因为它有点混乱)算法多一点。另外,如果你能告诉我你在updateValue中如何处理left right,我将不胜感激,因为它们对我来说没有意义,但事实上我有一种预感,它可能会起作用。我猜就是这样。非常感谢你所做的工作。