【问题标题】:Can anyone explain the solution of this memoization/dynamic programming prob/puzzle?谁能解释这个记忆/动态编程问题/难题的解决方案?
【发布时间】:2011-11-16 10:38:24
【问题描述】:

这是问题陈述:

这是一个两人游戏。最初,数组中有 n 个整数,玩家 A 和 B 有机会交替使用它们。每个玩家可以从数组的左端或右端取一个或多个数字,但不能一次从两端取。在他的时间里,他可以随心所欲地取任意数量的连续数字。当玩家从数组中取出所有数字时,游戏结束。每个玩家的分数是通过他所取的数字的总和来计算的。每个玩家都试图从其他玩家那里获得更多积分。如果两个玩家都发挥最佳,玩家 A 开始游戏,那么玩家 A 比玩家 B 能多获得多少分?

输入

输入由多个案例组成。每个案例都以指定整数n (0 < n ≤100) 的行开头,即数组中的元素数。之后,为游戏提供n 号码。输入由 n=0 所在的行终止。

输出

对于每个测试用例,打印一个数字,表示第一个玩家在最佳玩此游戏后获得的最大差异。

Sample Input                                Output for Sample Input

4

4 -10 -20 7                                 7

4

1 2 3 4                                     10

5

4 -10 -20 7 19                              12

0

这就是这个问题的解决方案。

#include<stdio.h>
#include<stdlib.h>
#define maxn 103

//typedef long long bg;
typedef long bg;

bg Table[maxn][maxn];
bg Seq[maxn];
bool Flag[maxn][maxn];
bg N;

bg Sum(int l, int r) {
    int i, sum = 0;
    for (i = l; i <= r; i++)
        sum += Seq[i];
    return sum;
}

bg Recur(int L, int R) {
    bg max, i, d, k;
    if (L == R)
        return Seq[L];
    if (Flag[L][R])
        return Table[L][R];
    max = Sum(L, R);
    d = Seq[L];
    for (i = L + 1; i <= R; i++) {
        k = Recur(i, R);
        if ((d - k) > max)
            max = d - k;
        d += Seq[i];
    }
    d = Seq[R];
    for (i = R - 1; i >= L; i--) {
        k = Recur(L, i);
        if ((d - k) > max)
            max = d - k;
        d += Seq[i];
    }
    Flag[L][R] = true;
    Table[L][R] = max;
    return max;
}

void Cal() {
    bg max, i, d, k;
    max = Sum(1, N);
    d = Seq[1];
    for (i = 2; i <= N; i++) {
        k = Recur(i, N);
        if ((d - k) > max)
            max = d - k;
        d += Seq[i];
    }
    d = Seq[N];
    for (i = N - 1; i >= 1; i--) {
        k = Recur(1, i);
        if ((d - k) > max)
            max = d - k;
        d += Seq[i];
    }
    printf("%ld\n", max);
}

void Reset() {
    for (int i = 1; i <= 100; i++) {
        for (int j = 1; j <= 100; j++)
            Flag[i][j] = false;
    }
}
int main() {
    //freopen("in.txt", "r", stdin);
    int i;
    while (scanf("%ld", &N) && N) {
        for (i = 1; i <= N; i++)
            scanf("%ld", &Seq[i]);
        Cal();
        Reset();
    }
    return 0;
}

我确实调试了它,但发现解决方案很难理解。

谁能解释一下这个问题的代码或解决方案。或者任何人都可以发布代码以使用动态编程而不是递归来解决这个问题?

【问题讨论】:

  • 是的,规则和代码都齐全了。
  • @Cephron - 有负数。有时取所有数字并不好。
  • @Petar - “他可以在他的时间里取任意多的连续数字。” - 我将其解释为,它可以从一端开始,只需通过连续的数字就可以到达另一端。 Edit 啊,好的。错过了负数。谢谢。

标签: c algorithm dynamic-programming memoization


【解决方案1】:

这里的状态是Table[left][right],如果您有一个包含从leftright 的元素的序列,它代表最佳解决方案。在每一步都尝试所有可能的移动 - 从左侧获取 N 元素或从右侧获取 N 元素,其中 N 介于 1right - left 之间。

Example:
4 -10 -20 7

从左边走:

表[1][4] = max(sum(1, 1) - 表[2][4], sum(1, 2) - 表[3][4], sum(1, 3) - 表[4][4], sum(1, 4))。

从右边走:

表[1][4] = max(sum(4, 4) - 表[1][3], sum(3, 4) - 表[1][2], sum(2, 4) - 表[1][1], sum(1, 4))。

sum(L, R)LR 之间的数组数之和。由于下一个对手回合,我正在减去。

【讨论】:

  • 能把计算步骤贴出来吗?
  • @guru - 您不清楚代码的哪一部分?您可以将print 语句放在Table[L][R] = max; 旁边,以自己查看步骤。尽管如此,我将添加一个简单的示例。
  • 我不懂递归,步骤。能否贴出解决的步骤,其实我看不懂。
  • @guru - 你首先了解动态编程和记忆吗?
  • @guru - 可以迭代地编写相同的算法。我个人觉得递归动态编程更容易理解。
猜你喜欢
  • 2022-11-10
  • 2011-01-09
  • 2021-01-08
  • 2013-04-24
  • 2022-01-16
  • 1970-01-01
  • 2015-11-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多