1 条题解
-
0
注意到,答案是一个形如 的序列的长度,其中 和 是某些整数。我们固定一个数(比如 ),然后枚举 ,并计算当 作为序列的最后一个数时能得到多长的答案。可以注意到,对于固定的 ,答案可以用贪心方法计算。我们同样采用这种思路:寻找在固定位置之前、且中间存在数 的最后一个 出现的位置,然后取该位置对应的答案长度再加 (可以用双指针法查找)。另外还需要考虑 是序列中第一次或第二次出现的情况。
此外,还有一种使用动态规划的解法。
两种解法的时间复杂度均为 。
如果有人能写出渐进复杂度更优的解法,我将非常高兴。
#include <bits/stdc++.h> using namespace std; const int N = 4005; int b[N]; int dp[N][N]; // dp[i][j]: 以 b[i] 和 b[j] 结尾的最长长度 (i < j) int last[N]; // 临时数组,记录某个值的最近出现位置 int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; for (int i = 1; i <= n; ++i) { cin >> b[i]; } int ans = 1; // 至少长度为 1 // 初始化 dp 数组 for (int i = 1; i <= n; ++i) { for (int j = i + 1; j <= n; ++j) { dp[i][j] = 2; // 至少两个数 } } // 枚举中间点 i for (int i = 1; i <= n; ++i) { // 清空 last 数组,注意值域 1e6,需要用 map 或重置技巧 // 这里用一个数组加时间戳的方法或直接用 map // 由于 n=4000,可以用 unordered_map 或 map,但 O(n^2 log) 可能稍慢 // 更高效:用 vector 记录出现过的值,然后重置 // 简便起见,用 unordered_map unordered_map<int, int> last; for (int j = i + 1; j <= n; ++j) { // 在 i 之前找值为 b[j] 的位置 if (last.count(b[j])) { int k = last[b[j]]; dp[i][j] = max(dp[i][j], dp[k][i] + 1); } last[b[i]] = i; // 更新 b[i] 最近出现的位置为 i // 注意这个更新要在查询之后,避免自己匹配自己 ans = max(ans, dp[i][j]); } } cout << ans << endl; return 0; }
- 1
信息
- ID
- 6981
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者