codeforces 1110D-Jongmah

题意

n个数,皆不大于m,(1 /leq n, m \leq 10^6),求能构成的顺子和刻子的数目和的最大值。

题解

先考虑简化版,每个数只出现一遍,那明显是个水题。
然后考虑能不能限制每个数出现的次数来简化问题。全部设置不大于3次其实是不行的,毕竟优先考虑构成顺子或刻子是不行的。
例如数据1,2,2,2,3和1,2,3,3,3,4,4,5,5
但是可以注意到,三个或三个以上的相同顺子,如3个三元组(2, 3, 4),可以看成是(2,2,2)和(3,3,3)(4,4,4)这样,所以每个三元组出线不超过2次,所以转移状态有限。
最开始是想用dp[maxm][3]的二维数字做的,但是发现不行,所以用的是三维dp
与i有关的三元顺子,有(i-2, i-1, i)和(i-1,i,i+1)和(i, i+1,i+2),考虑如何转移到包含i+1的状态,所以dp的时候只考虑后两者。
dp[i][j][k]指的是当前使用后两者的数量分别是j和k,此时剩余数字i+1的数量是A[i+1]-j-k,更新dp[i+1][k][:]的状态

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;

const int maxn = 1e6 + 10;
int dp[maxn][3][3];
int A[maxn];
int n, m;

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	scanf("%d%d", &n, &m);
	memset(A, 0, sizeof(A));
	memset(dp, -1, sizeof(dp));
	dp[0][0][0] = 0;
	int t;
	for (int i = 0; i < n; i++) {
		scanf("%d",&t);
		A[t]++;
	}
	for (int i = 0; i <= m + 1; i++) {
		for (int j = 0; j <= 2; j++) {
			for (int k = 0; k <= 2; k++) {
				if (dp[i][j][k] == -1) continue;
				int up = A[i + 1] - j - k;
				for (int tt = 0; tt <= 2 && tt <= up; tt++) {
					dp[i + 1][k][tt] = max(dp[i + 1][k][tt], dp[i][j][k] + (up - tt) / 3 + tt);
				}
			}
		}
	}
	printf("%d\n", dp[m + 1][0][0]);
	return 0;
}
posted @ 2019-07-13 20:11  xFANx  阅读(225)  评论(0编辑  收藏  举报