CodeForces 1110 D. Jongmah

本文介绍了一种优化动态规划(DP)算法的方法,用于解决给定数列的最优分组问题,目标是最小化分组数量,使得每组内的元素递增或相同。通过限制每个元素出现的最大次数,将复杂度降至O(n*9^2*2),并使用滚动数组优化空间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门.

看错题交了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(n922),空间用滚动优化,常数级别。

反正能跑的过。

为了求稳,就不要搞上界优化了,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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值