P2734 游戏 A Game

P2734 游戏 A Game

\(N\) 个正整数的序列放在一个游戏平台上,游戏由玩家1开始,两人轮流从序列的任意一段取一个数,取数后该数字被去掉并累加到本玩家的得分中,当数取尽时,游戏结束。现在假设两个玩家都采取最优策略,输出最优玩家一和玩家二的最终分数?

例如: \(v = \{8,15,3,7\}\) ,先手拿 \(7\) ,对手拿 \(8\) ;先手再拿 \(15\) ,对手拿 \(3\) 结束,先手拿到的最大价值为 \(7 + 15 = 22\)

思路:

贪心?

显然不行,刚刚的案例如果用贪心的话,先手第一次拿 \(8\) 对手一定拿 \(15\) ,最终先手的值肯定不如刚刚的选法。

想到每次可以选 \(a[i]\) 或者 \(a[j]\) (这里的 \(i\),\(j\) 是指此时的头尾)。

定义 \(f[i][j]\) 为当前的人采取最优策略得到的值,存的值为玩家一与玩家二的差值。

就可以得到转移方程:

\(f[i][j] = max(f[i + 1][j] + a[i],f[i,j - 1] + a[j]])\) (如果当前是玩家一的话)

\(f[i][j] = min(f[i + 1][j] - a[i],f[i,j - 1] - a[j]])\) (如果当前是玩家一的话)

可以通过当前 \(i + j\) 的奇偶性来判断或者传一个参数来标记当前是谁选择。

然后因为是不断向中间逼近,所以这里采用自顶向下的记忆化搜索来写。

最后得到的答案 \(f[1][n]\) 是玩家一和玩家二的差值。

假设玩家一的最终值为 \(x\) ,玩家二的最终值为 \(y\)

  • 两个玩家的总和一定为全部元素之和: \(x + y = sum\)
  • 玩家一和玩家二的差值: \(x - y = f[1][n]\)

通过两个公式就可以得到两个变量的具体值了。

实现:

#include <bits/stdc++.h>
using namespace std;
const int N = 105, Min = -1e9;
int a[N], n;
int f[N][N];
int dfs(int u, int i, int j)
{
    if (i == j)
    {
        if (u)
            return f[i][j] = a[i];
        else
            return f[i][j] = -a[i];
    }

    if (f[i][j] != Min)
        return f[i][j];

    if (u)
        f[i][j] = max(dfs(u ^ 1, i + 1, j) + a[i], dfs(u ^ 1, i, j - 1) + a[j]);
    else
        f[i][j] = min(dfs(u ^ 1, i + 1, j) - a[i], dfs(u ^ 1, i, j - 1) - a[j]);

    return f[i][j];
}

int main()
{
    scanf("%d", &n);
    int sum = 0;
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        sum += a[i];
    }
    for (int j = 0; j <= n; j++)
        for (int k = 0; k <= n; k++)
            f[j][k] = Min;

    dfs(1, 1, n);
    int dif = f[1][n];
    int rex = (sum + dif) / 2;
    printf("%d %d\n", rex, sum - rex);
    return 0;
}
posted @ 2022-12-22 17:01  zxr000  阅读(25)  评论(0编辑  收藏  举报