当前位置:首页 > 比赛题解 > 动态规划实战:洛谷P10111(2023GESP七级)纸牌游戏

动态规划实战:洛谷P10111(2023GESP七级)纸牌游戏

11小时前比赛题解26

截图未命名.jpg 动态规划实战:洛谷P10111(2023GESP七级)纸牌游戏 动态规划 C++ 洛谷题解 GESP GESP七级 第1张

一、问题理解与建模

这道题目描述了一个特殊的纸牌游戏:

  1. 双方各有0、1、2三张牌

  2. 游戏进行N轮,每轮双方出牌

  3. 胜负规则:1胜0,2胜1,0胜2

  4. 得分规则:

    • 胜者得2×aᵢ分

    • 败者不得分

    • 平局各得aᵢ分

  5. 特殊规则:

    • 对手的出牌顺序已知

    • 玩家从第2轮开始,要么保持上一轮的牌,要么换牌(有换牌惩罚)

我们需要计算玩家能获得的最大分数,考虑换牌的惩罚。

二、算法选择

这是一个典型的动态规划问题,因为:

  1. 问题可以分解为子问题(每轮的选择)

  2. 有重叠子问题(相同的状态会重复出现)

  3. 有最优子结构(全局最优解包含局部最优解)

我们定义DP[i][j][k]表示:

  • 进行到第i轮

  • 当前出牌为j

  • 已经换牌k次
    时的最大得分。

三、完整代码

#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;

// 判断胜负关系,返回得分
int get_score(int my_card, int yang_card, int a) {
    if (my_card == yang_card) return a; // 平局
    if ((my_card == 1 && yang_card == 0) ||
        (my_card == 2 && yang_card == 1) ||
        (my_card == 0 && yang_card == 2)) {
        return 2 * a; // 获胜
    }
    return 0; // 失败
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int N;
    cin >> N;
    
    vector<int> a(N), b(N-1), c(N);
    for (int i = 0; i < N; ++i) cin >> a[i];
    for (int i = 0; i < N-1; ++i) cin >> b[i];
    for (int i = 0; i < N; ++i) cin >> c[i];
    
    // dp[i][j][k]: 第i轮出牌j,换了k次牌的最大得分
    vector<vector<vector<int>>> dp(N, vector<vector<int>>(3, vector<int>(N, INT_MIN)));
    
    // 初始化第一轮
    for (int j = 0; j < 3; ++j) {
        dp[0][j][0] = get_score(j, c[0], a[0]);
    }
    
    // 动态规划
    for (int i = 1; i < N; ++i) {
        for (int prev_j = 0; prev_j < 3; ++prev_j) {
            for (int prev_k = 0; prev_k < N; ++prev_k) {
                if (dp[i-1][prev_j][prev_k] == INT_MIN) continue;
                
                // 不换牌
                int score = get_score(prev_j, c[i], a[i]);
                if (dp[i][prev_j][prev_k] < dp[i-1][prev_j][prev_k] + score) {
                    dp[i][prev_j][prev_k] = dp[i-1][prev_j][prev_k] + score;
                }
                
                // 换牌(不能超过N-1次)
                if (prev_k < N-1) {
                    for (int new_j = 0; new_j < 3; ++new_j) {
                        if (new_j == prev_j) continue;
                        score = get_score(new_j, c[i], a[i]);
                        int new_score = dp[i-1][prev_j][prev_k] + score - b[prev_k];
                        if (dp[i][new_j][prev_k+1] < new_score) {
                            dp[i][new_j][prev_k+1] = new_score;
                        }
                    }
                }
            }
        }
    }
    
    // 找出最大得分
    int max_score = INT_MIN;
    for (int j = 0; j < 3; ++j) {
        for (int k = 0; k < N; ++k) {
            max_score = max(max_score, dp[N-1][j][k]);
        }
    }
    
    cout << max_score << endl;
    
    return 0;
}


四、代码解析

  1. 输入处理‌:读取游戏轮数N,每轮得分a,换牌惩罚b,对手出牌序列c。

  2. 得分计算函数‌:get_score()根据出牌判断胜负并计算得分。

  3. 动态规划初始化‌:

    • 三维数组dp[i][j][k]存储状态

    • 初始化第一轮三种出牌可能

  4. 状态转移‌:

    • 不换牌:保持上一轮的牌,得分累加

    • 换牌:选择不同的牌,扣除相应惩罚,得分累加

  5. 结果提取‌:遍历最后一轮所有可能状态,找出最大得分

通过这个例子,我们学习了如何使用动态规划解决带约束的最优化问题。动态规划是解决许多实际问题的强大工具,值得深入学习和掌握。


原创内容 转载请注明出处

分享给朋友:

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。