看错题交了11发才过。
给出n个数,分最多组,使得每组标号相邻递增或全部相同。
1<=n<=10^6
首先自然地想到dp设前两个有多少个,当然这个dp会超时。
不难想到如果一个数出现的次数比较多,那么应该先把他自己取了。
那么这个数具体是多少呢?
如果我们取了(a,b,c)三次,是可以转成取a,b,c分别三次。
那么一个数最多作为前中后分别2次,最多的次数就是2*3=6次
也就是说如果这个数出现了y次,那么:
while(y-3>=6) y-=3;
这样状态就比较少了,再卡一下,直接暴力dp的复杂度是 O ( n ∗ 9 2 ∗ 2 ) O(n*9^2*2) O(n∗92∗2),空间用滚动优化,常数级别。
反正能跑的过。
为了求稳,就不要搞上界优化了,WA了好多发。
Code:
#include<cstdio>
#include<cstring>
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define pp printf
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
using namespace std;
const int N = 1e6 + 5;
const int c = 8;
int n, m, a[N], b[N], ans, sum;
int f[2][c + 1][c + 1], o, la1, la2;
int main() {
scanf("%d %d", &n, &m);
fo(i, 1, n) scanf("%d", &a[i]), b[a[i]] ++;
memset(f[o], 128, sizeof f[o]); f[o][0][0] = 0;
la1 = 0; la2 = 0;
fo(i, 1, m) if(b[i]) {
while(b[i] > 8) b[i] -= 3, sum ++;
memset(f[!o], 128, sizeof f[!o]);
if(la1 == la2 - 1 && la2 == i - 1) {
fo(u, 0, c) fo(v, 0, c) if(f[o][u][v] >= 0) {
fo(j, 0, min(2, min(u, min(v, b[i])))) {
f[!o][v - j][b[i] - j] = max(f[!o][v - j][b[i] - j], f[o][u][v] + j);
}
}
} else {
fo(u, 0, c) fo(v, 0, c)
f[!o][v][b[i]] = max(f[!o][v][b[i]], f[o][u][v]);
}
la1 = la2; la2 = i;
o = !o;
fo(u, 0, c) fo(v, 0, c) if(f[o][u][v] >= 0)
fo(j, 0, u / 3) fo(k, 0, v / 3) f[o][u - j * 3][v - k * 3] = max(f[o][u - j * 3][v - k * 3], f[o][u][v] + j + k);
}
fo(u, 0, c) fo(v, 0, c) ans = max(ans, f[o][u][v]);
pp("%d", ans + sum);
}