题目
算法标签: 动态规划, 线性 d p dp dp, 贪心, 区间 d p dp dp
思路
定义状态表示 f [ i ] [ j ] f[i][j] f[i][j]表示左边界是 i i i并且合并出的最大值是 j j j的右边界是什么位置(开区间), k k k区间覆盖的是 [ i , f [ i ] [ j ] − 1 ] [i, f[i][j] - 1] [i,f[i][j]−1], 如何进行状态转移?
如果当前状态 f [ i ] [ k ] = 0 f[i][k] = 0 f[i][k]=0, 可以尝试合并两个 k − 1 k - 1 k−1达到当前状态, 设 r = f [ i ] [ k − 1 ] r = f[i][k - 1] r=f[i][k−1], 那么 f [ i ] [ k ] = f [ r ] [ k − 1 ] f[i][k] = f[r][k - 1] f[i][k]=f[r][k−1], 如果最后计算出 f [ i ] [ k ] ≠ 0 f[i][k] \ne 0 f[i][k]=0那么可以记录到答案中, 状态转移依赖 f [ i ] [ k − 1 ] f[i][k - 1] f[i][k−1], 可以对 k k k从小到大枚举, 递推就能计算出当前状态, 算法时间复杂度 O ( n m ) O(nm) O(nm)
代码
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 270010, M = 60;
int n, ans, f[N][M];
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 1; i <= n; ++i) {
int val;
cin >> val;
f[i][val] = i + 1;
}
//以i为左端点合并出数字j的右端点是f[i][j]
for (int k = 2; k < M; ++k) {
for (int i = 1; i <= n; ++i) {
if (!f[i][k]) f[i][k] = f[f[i][k - 1]][k - 1];
if (f[i][k]) ans = k;
}
}
cout << ans << "\n";
return 0;
}
为什么 M = 60 M = 60 M=60
注意到 N = 262144 N = 262144 N=262144, 也就是 2 18 2 ^ {18} 218, 每个数的范围不超过 40 40 40, 最大的结果就是 40 + 18 = 58 40 + 18 = 58 40+18=58, 向上取整就是 60 60 60

京公网安备 11010502036488号