嘘~ 正在从服务器偷取页面 . . .

洛谷P2734 [USACO3.3] 游戏 A Game 题解


洛谷P2734 [USACO3.3] 游戏 A Game 题解

题目链接:P2734 [USACO3.3] 游戏 A Game

题意

有如下一个双人游戏:\(N (2 \leq N \leq 100)\) 个正整数的序列放在一个游戏平台上,游戏由玩家 \(1\) 开始,两人轮流从序列的任意一端取一个数,取数后该数字被去掉并累加到本玩家的得分中,当数取尽时,游戏结束。以最终得分多者为胜。

编一个执行最优策略的程序,最优策略就是使玩家在与最好的对手对弈时,能得到的在当前情况下最大的可能的总分的策略。你的程序要始终为第二位玩家执行最优策略。

输入格式

第一行: 正整数 \(N\),表示序列中正整数的个数。

第二行至末尾: 用空格分隔的 \(N\) 个正整数(\(1\le N \le 200\))。

输出格式

只有一行,用空格分隔的两个整数: 依次为玩家一和玩家二最终的得分。

先讲 dp 解法,还是很巧妙的,需要对博弈论理解深刻。

如果我们设 \(f_{i,j}\) 表示区间 \([i,j]\) 先手的最优方案,那么有 \[ f_{i,j} = S_j - S_{i-1}-\min\{f_{i+1,j},~f_{i,j-1}\} \] 其中 \(S_i = \sum_{j=1}^{i}a_j\) 为前缀和。

为什么需要减去 \(f\) 呢?因为如果先手取了 \(a_i\) ,那后手的最优方案就是 \(f_{i+1,j}\) ,因为先后手转换了。

时间复杂度 \(\mathcal{O}(n^2)\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(105))

int n, a[N], sum[N], dp[N][N];
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n;
    for(int i = 1; i <= n; i++) {
        cin >> a[i]; sum[i] = a[i] + sum[i - 1]; dp[i][i] = a[i];
    }
    for(int i = n - 1; i; i--)
        for(int j = i + 1; j <= n; j++) {
            dp[i][j] = sum[j] - sum[i - 1] - min(dp[i + 1][j], dp[i][j - 1]);
        }
    cout << dp[1][n] << ' ' << sum[n] - dp[1][n] << '\n';
    return 0;
}


然后来讲贪心的解法。

不知道大家有没有觉得这道题很熟悉,之前有道题也是两头取,但是中间的数是存在一定单调性的。

考虑最简单的情况,即 \(n=3\) 时,若 \(a_2 > a_1\)\(a_2 > a_3\)

那么先手能比后手多取到 \(a_1 + a_3 - a_2\) ,于是我们把它看作一个数

即对于所有 \(a_i > a_{i-1}\)\(a_{i} > a_{i + 1}\)\(i\) ,我们把 \(a_{i-1},~a_{i},~a_{i+1}\) 缩成一个数 \(a_{i+1}+a_{i-1}-a_i\)

重复执行这个过程直到 \(a_i\) 成为关于 \(i\) 的单谷函数,这样我们就可以贪心地从两头取了。

这样的做法唯一奇怪的地方就是我们真的可以这么缩这些数吗?事实上是可以的。

证明:

对于某个满足条件的 \(i\) ,我们将它缩数后,它可能成为单谷函数的某一侧甚至谷部

当然它也可能成为一个新的峰的某一部分,而最终我们会将每个峰都拍成谷的一部分。\(\square\)

时间复杂度 \(\mathcal{O}(n)\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(1e6 + 15))

int sum,top,stk[N];
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int n; cin >> n;
    for(int i = 1, x; i <= n; i++)
    {
        cin >> x; sum += x; stk[++top] = x;
        while(top >= 3 && stk[top - 1] >= stk[top] && stk[top - 1] >= stk[top - 2]) {
            stk[top - 2] = stk[top] + stk[top - 2] - stk[top - 1], top -= 2;
        }
    }
    int l = 1, r = top, res = 0;
    while(l <= r)
    {
        if(stk[l] > stk[r]) res += stk[l++]; else res += stk[r--];
        if(l > r) break;
        if(stk[l] > stk[r]) res -= stk[l++]; else res -= stk[r--];
    }
    cout << (sum + res) / 2 << ' ' << (sum - res) / 2 << '\n';
    return 0;
}

参考文献

[1] https://www.luogu.com.cn/article/v58woiz8

[2] https://www.luogu.com.cn/article/bp03gipm

洛谷博客都换新样了,我的生活什么时候换新样呢。


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录