序列

题目大意:给定一个长度为n的数列.初始时数列中每个元素都不大于40,你可以在其上进行若干次操作.在一次操作中,你会选出相邻且相等的两个元素,把它们合并成一个元素,新的元素值为旧元素值+1.请你找出,怎样的一系列操作可以让数列中的最大值变得尽可能地大?这个最大值是多少?

一开始往数据结构上想,想错了,正解是dp

luogu有原题248G

对于弱化版的,直接\(O(N^2)\)就行

如下为我自己推的dp

状态:设\(f_i\)表示在前i个数中,所能合成的最大的数字,其中第i个位置参不参与合成都可以

转移:

\(f_i = f_{i-1}\)即第i个数字不参与合成,那么答案就是直接前i-1的

\(f_i =...\)把第i个数字压入栈,然后不断往前更新,合并.最后对栈里的元素取max

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
int n, top;
int a[N], f[N], sta[N];

inline int func(int x)
{
int i, res(0);
top = 0, memset(sta, 0, sizeof sta);
sta[++ top] = a[x];
for (i = x - 1; i >= 1; -- i)
{
if (a[i] == sta[top])
{
++ sta[top];
while (sta[top] == sta[top - 1]) sta[top --] = 0, ++ sta[top];
}
else sta[++ top] = a[i];
}
for (i = 1; i <= top; ++ i) res = max(res, sta[i]);
return res;
}

signed main()
{
int i; n = read();
for (i = 1; i <= n; ++ i) a[i] = read();

f[1] = a[1];
for (i = 2; i <= n; ++ i)
{
f[i] = f[i - 1];
f[i] = max(f[i], func(i));
}
put(f[n]);

return 0;
}

再来考虑可以\(O(N\times K)\)算的

状态很奇妙:\(f_{i,j}\)表示到第i个位置,能否合成j这个数字,如果可以合成,那么他的后继是谁

转移:

\(f_{i,j-1}\;and\;f_{f_{i,j-1},j-1}\)都存在的话

\(f_{i,j} = f_{f_{i,j-1},j-1}\)(这里记录的是"后继",也可以记录"前驱")

然后转移的时候不断更新ans

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <bits/stdc++.h>
const int N = 3e5;

int n, res;
int f[N][66];

signed main()
{
int i, j; n = read();
for (i = 1; i <= n; ++ i) f[i][read()] = i + 1;

for (j = 2; j <= 60; ++ j)
{
for (i = 1; i <= n; ++ i)
{
if (f[i][j - 1] && f[f[i][j - 1]][j - 1])
{
f[i][j] = f[f[i][j - 1]][j - 1], res = j;
}
}
}
put(res);
return 0;
}