牛客网23954题:用动态规划解决队列得分问题
一、题目解读
这道题目要求我们处理一组元素,每个元素属于一个集合(set)并有一个分值(value)。我们需要从中选择一个子序列,满足:
子序列中相邻元素属于相同集合时获得10分奖励
最终得分为元素分值总和加上所有相邻相同集合的奖励
在获得最大得分的同时,要求子序列长度最短
这是一个典型的动态规划问题,需要同时考虑得分最大化和序列长度最小化两个目标。
二、解题思路
本解法采用动态规划策略,核心思路如下:
定义DP[i][j]表示前i个元素中以集合j结尾的子序列的最大得分和对应最小长度
对于每个元素,考虑三种情况:
不选当前元素,直接继承前一个状态
选当前元素作为子序列的第一个元素
选当前元素作为子序列的非第一个元素,需要考虑与前一个元素的集合关系
最终遍历所有可能的结尾集合,找出最大得分及对应的最小长度
三、解题步骤
初始化:读取输入数据,定义元素结构体
初始化DP数组:dp[i][j]初始值为INT_MIN,表示不可达状态
处理第一个元素:初始化所有可能的状态起点
状态转移:
不选当前元素的情况
选当前元素作为子序列开头的情况
选当前元素作为子序列延续的情况(考虑集合相同奖励)
结果提取:遍历所有可能的结尾状态,找出最大得分和最小长度
输出结果
四、完整代码及注释
#include <iostream> #include <vector> #include <climits> using namespace std; struct Element { int set; int value; }; int main() { int N; cin >> N; vector<Element> elements(N); for (int i = 0; i < N; ++i) { cin >> elements[i].set >> elements[i].value; } // dp[i][j]表示前i个元素中以set j结尾的最大得分和最小长度 vector<vector<pair<int, int>>> dp(N, vector<pair<int, int>>(21, {INT_MIN, 0})); // 初始化第一个元素 dp[0][elements[0].set] = {elements[0].value, 1}; for (int i = 1; i < N; ++i) { int current_set = elements[i].set; int current_value = elements[i].value; // 不选当前元素的情况 for (int j = 1; j <= 20; ++j) { dp[i][j] = dp[i-1][j]; } // 选当前元素作为第一个元素 if (current_value > dp[i][current_set].first) { dp[i][current_set] = {current_value, 1}; } else if (current_value == dp[i][current_set].first) { dp[i][current_set].second = min(dp[i][current_set].second, 1); } // 选当前元素作为非第一个元素 for (int prev_set = 1; prev_set <= 20; ++prev_set) { if (dp[i-1][prev_set].first == INT_MIN) continue; int bonus = (prev_set == current_set) ? 10 : 0; int new_score = dp[i-1][prev_set].first + current_value - bonus; int new_length = dp[i-1][prev_set].second + 1; if (new_score > dp[i][current_set].first) { dp[i][current_set] = {new_score, new_length}; } else if (new_score == dp[i][current_set].first) { dp[i][current_set].second = min(dp[i][current_set].second, new_length); } } } // 找出所有可能的最大得分 int max_score = INT_MIN; int min_length = INT_MAX; for (int j = 1; j <= 20; ++j) { if (dp[N-1][j].first > max_score) { max_score = dp[N-1][j].first; min_length = dp[N-1][j].second; } else if (dp[N-1][j].first == max_score) { min_length = min(min_length, dp[N-1][j].second); } } cout << max_score << " " << min_length << endl; return 0; }
五、总结
本文详细解析了牛客网23954题的动态规划解法,该问题要求在选择元素子序列时同时考虑得分最大化和长度最小化。通过定义合适的状态表示和状态转移方程,我们能够高效地解决这个问题。解法时间复杂度为O(N20),空间复杂度也为O(N20),在题目给定的约束条件下表现良好。
原创内容 转载请注明出处