1 条题解
-
0
题意理解
我们有一个长度为 的正整数数组 ,要给每个数染成红色或蓝色。
对于每个位置 :
- 如果 左边没有同色的数,则 。
- 否则,设左边最近的同色数是 :
- 如果 ,则 ;
- 否则 。
得分是 ,我们要最大化这个得分。
关键观察
-
得分来源
只有当某个位置 和它左边最近的同色位置 的值相等时,才能得到 的分数。
也就是说,得分来源于相邻同色的相同数值对(相邻指的是在染色序列中相邻的同色位置,中间没有同色的数)。 -
染色策略
我们可以把数组分成若干段,每一段内部染成同一种颜色,并且相邻段颜色不同。
这样,每一段内部的相邻相同数值对都可以贡献分数。 -
问题转化
假设我们有一个子数组 染成同色,那么对于这个子数组:- 第一个位置 没有左边同色数,所以 。
- 对于 到 ,如果 ,则 ,否则 。
所以这一段的总得分就是 。
-
分段与颜色交替
我们可以把整个数组分成若干段,每段同色,相邻段异色。
得分就是所有段内部相邻相等数值的 之和。 -
最优分段
问题变成:在数组上切若干刀,把数组分成若干段,最大化所有段内部的相邻相等数值的 之和。
注意:段长度至少为 1。 -
动态规划
设 表示考虑前 个元素的最大得分。
转移:- 要么 单独成一段:。
- 要么 和前面若干元素成一段:如果 ,则从 转移过来并加上 ;更一般地,如果从 到 成一段,那么这段的得分是 。
但这样直接做是 的,不可行。
最终算法
- 初始化两个数组 和 (字典或数组,因为 ,可以开数组大小 ),表示当前以红色/蓝色结尾,且末尾值是 时的最大得分。
- 初始化 。
- 遍历 到 :
- 计算新状态:
- 如果 染红色:
- 从 中所有值中取最大值 (即上一个颜色是蓝色的最大得分),作为不配对的情况。
- 如果 存在,则配对得分 = 。
- 新红色得分 = 。
- 同理处理蓝色。
- 如果 染红色:
- 更新 和 。
- 更新 。
- 计算新状态:
- 输出 。
这样复杂度 ,空间 。
算法标签
- 动态规划
- 贪心优化
- 状态压缩
样例验证
样例1:
1 2 1
最优:1(红),2(蓝),1(红)
得分:第三个1和第一个1同色且中间无红,相等,得1。
输出1,符合。样例2:
1 2 3 4
无相等对,输出0。样例3:
3 5 2 5 1 2 1 4
输出8,符合。
最终题解:
我们通过动态规划,维护以某种颜色结尾且末尾为某数值时的最大得分,实现 的解法。
#include <bits/stdc++.h> using namespace std; const int MAX_A = 1e6 + 5; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin >> T; while (T--) { int n; cin >> n; vector<int> A(n); for (int i = 0; i < n; i++) { cin >> A[i]; } vector<int> lastR(MAX_A, -1), lastB(MAX_A, -1); int bestR = 0, bestB = 0; for (int i = 0; i < n; i++) { int v = A[i]; int newR = max(bestB, (lastR[v] != -1 ? lastR[v] + v : bestB)); int newB = max(bestR, (lastB[v] != -1 ? lastB[v] + v : bestR)); lastR[v] = max(lastR[v], newB); lastB[v] = max(lastB[v], newR); bestR = max(bestR, newR); bestB = max(bestB, newB); } cout << max(bestR, bestB) << "\n"; } return 0; }
- 1
信息
- ID
- 5648
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 10
- 标签
- 递交数
- 10
- 已通过
- 1
- 上传者