约定:称形如 \([a-1,a,a+1]\) 这样的三元组为关于 \(a\) 的顺子,形如 \([a,a,a]\) 这样的三元组为关于 \(a\) 的对子。
注意到如果一个关于 \(a\) 的顺子出现三次,则可以将其拆分为三个分别关于 \(a-1,a,a+1\) 的对子。
考虑DP:
设 \(c_i\) 为 \(i\) 的个数。
设 \(f_{i,j,k}\) 为考虑到数 \(i\),关于 \(i\) 的顺子有 \(j\) 个,关于 \(i+1\) 的顺子有 \(k\) 个的最多三元组个数,根据上面的分析,\(j,k\in \{0,1,2\}\)。
状态转移方程:\(f_{i,k,t}=max_{j\in \{0,1,2\},c_i-j-k-t\geq 0}\ (f_{i-1,j,k}+(c_i-j-k-t)/3+t)\)
优化和技巧:
DP到第 \(m+2\) 个,目标:\(ans=f_{m+2,0,0}\)
滚动数组优化空间,\(f\) 数组就可以只开233 (雾,因此目标: \(ans=f_{(m+2)\&1,0,0}=f_{m\&1,0,0}\)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 6;
int n, m, c[N], f[2][3][3];
int main() {
cin >> n >> m;
while (n--) {
int x;
scanf("%d", &x);
++c[x];
}
for (int i = 1; i <= m + 2; i++) {
int p = i & 1, q = p ^ 1;
memset(f[p], 0, sizeof(f[p]));
for (int j = 0; j < 3; j++)
for (int k = 0; k < 3; k++)
for (int t = 0; t < 3; t++)
if (c[i] - j - k - t >= 0)
f[p][k][t] = max(f[p][k][t], f[q][j][k] + (c[i] - j - k - t) / 3 + t);
}
cout << f[m&1][0][0] << endl;
return 0;
}