【问题标题】:Algorithm for coin game硬币游戏算法
【发布时间】:2021-03-12 19:29:51
【问题描述】:

我有一个问题来解决这个问题。

给两个玩家玩游戏的 N 个硬币。硬币有不同的价值。每个玩家每回合可以选择 1 或 2 个硬币。硬币价值较大的玩家获胜。玩家只能从数组的开头或结尾挑选硬币。两位选手都打得很好。

例如

Input:
-11 5 3 6 -1
Output:
A (-1)
B (6,3)
A (5)
B (-11)
A: 4, B: -2

Input:
-34 -1 94 111 43 78 -79 13
Output:
A (13)
B (-79,78)
A (43,111)
B (94,-1)
A (-34)
A: 133, B: 92

如果有人能帮助我,我会很高兴。我发现了各种关于算法的帖子,其中玩家只能选择一枚硬币,但没有更多。谢谢!

【问题讨论】:

  • 你知道示例输入输出背后的逻辑吗?
  • @ChetanRanpariya 我想不出算法
  • 一个详尽的 Mini-Max 肯定可以。也许有更智能的算法。
  • 所以有些硬币有负值?
  • @chux-ReinstateMonica 听起来左-左、右-右也可以?或者 op 省略了它的限制

标签: c algorithm recursion


【解决方案1】:

使用minimax 找到最佳移动。这会告诉你谁赢了,如果你遵循最好的动作,也显示出完美的发挥。

在每个时刻,最多有 5 个可用的移动(使用start 作为数组中第一个硬币的索引,end 作为最后一个硬币之后的索引):

  • 你可以从一开始就拿硬币:coins += array[start++]
  • 最后一个:coins += array[--end]
  • 各一个(仅当 2 个或更多时):coins += array[start++] + array[--end]
  • 从头开始两个(仅当 2 个或更多;如果只有 2,则相当于上一个):* 每个从一个(仅当 2 个或更多):coins += array[start++] + array[start++]
  • 或从末尾开始两个(仅当 2 个或更多;如果只有 2,则相当于前 2 个):coins += array[--end] + array[--end]

通过上述移动生成,并在递归之前更改startend,实现维基百科(或任何其他来源)中的伪代码应该很简单。我喜欢等效的negamax 变体,因为它实现起来更简单。

【讨论】:

    【解决方案2】:

    你可以通过动态规划来解决它。

    a 表示输入元素n=length(a)。 令f[i,j]表示取前i个硬币和最后(nj)个硬币时S(Pl1)-S(Pl2)的最大结果,g[i,j]表示S(Pl1)-S(Pl2)的最小结果.

    f[i,j] = max{g[i+1,j]+a[i], g[i+2,j]+a[i]+a[i+1], g[i+1,j-1]+a[i]+a[j], g[i,j-2]+a[j]+a[j-1], g[i,j-1]+a[j]}
    g[i,j] = min{f[i+1,j]-a[i], f[i+2,j]-a[i]-a[i+1], ...}
    

    这个想法是剩余硬币的最佳策略独立于其他硬币的获取方式。

    函数可以通过将g[i,j]替换为-f[i,j]来简化,因为gf之间的唯一区别是取币的顺序。

    这里是幼稚的 C 实现:

    #include <stdio.h>
    #include <string.h>
    
    typedef struct record{
        int lDelta, rDelta;
        int score;
    };
    
    void checkAndUpdate(int value, int* addr, struct record *rec, int ld, int rd, int s) {
        if (value > *addr) {
            *addr = value;
            rec->lDelta = ld;
            rec->rDelta = rd;
            rec->score = s;
        }
    };
    
    int main() {
        int n;
        scanf("%d", &n);
        int a[n];
        for (int i = 0; i < n; ++i)
            scanf("%d", a + i);
    
        int f[n][n];
        memset(f, 128, sizeof(f));
        struct record r[n][n];
    
        for (int i = 0; i < n; ++i) {
            if (i > 0)
                f[i][i-1] = 0;
            checkAndUpdate(a[i], &f[i][i], &r[i][i], 1, 0, a[i]);
        }
        for (int l = 1; l < n; ++l) {
            for (int i = 0; i + l < n; ++i) {
                checkAndUpdate(-f[i + 1][i + l] + a[i], &f[i][i + l], &r[i][i + l], 1, 0, a[i]);
                checkAndUpdate(-f[i + 2][i + l] + a[i] + a[i + 1], &f[i][i + l], &r[i][i + l], 2, 0, a[i] + a[i + 1]);
                checkAndUpdate(-f[i + 1][i + l - 1] + a[i] + a[i + l], &f[i][i + l], &r[i][i + l], 1, 1, a[i] + a[i + l]);
                checkAndUpdate(-f[i][i + l - 2] + a[i + l - 1] + a[i + l], &f[i][i + l], &r[i][i + l], 0, 2, a[i + l - 1] + a[i + l]);
                checkAndUpdate(-f[i][i + l - 1] + a[i + l], &f[i][i + l], &r[i][i + l], 0, 1, a[i + l]);
            }
        }
    
        int lB = 0, rB = n - 1;
        int pl = 1;
        int score[2];
        memset(score, 0, sizeof(score));
        while (lB != rB) {
            printf("PL%d take %d from the left side, %d from the right side, current score=%d\n", pl, r[lB][rB].lDelta, r[lB][rB].rDelta, score[pl-1]+=r[lB][rB].score);
            int tmp = r[lB][rB].lDelta;
            rB -= r[lB][rB].rDelta;
            lB += tmp;
            pl = 3 - pl;
        }
        printf("PL%d takes the remaing coins, score=%d\n", pl, score[pl-1]+=r[lB][rB].score);
        printf("Final score:\tPL1=%d, Pl2=%d\t\n", score[0], score[1]);
    }
    

    运行结果:

    Input:
    5
    -11 5 3 6 -1
    
    Output:
    PL1 take 0 from the left side, 1 from the right side, current score=-1
    PL2 take 0 from the left side, 2 from the right side, current score=9
    PL1 take 0 from the left side, 1 from the right side, current score=4
    PL2 takes the remaing coins, score=-2
    Final score:    PL1=4, Pl2=-2
    
    Input:
    8
    -34 -1 94 111 43 78 -79 13
    
    Output:
    PL1 take 0 from the left side, 1 from the right side, current score=13
    PL2 take 0 from the left side, 2 from the right side, current score=-1
    PL1 take 0 from the left side, 2 from the right side, current score=167
    PL2 take 0 from the left side, 2 from the right side, current score=92
    PL1 takes the remaing coins, score=133
    Final score:    PL1=133, Pl2=92
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-04
      • 1970-01-01
      • 1970-01-01
      • 2015-12-20
      相关资源
      最近更新 更多