【题意】
给n个数字,数字的范围为m,连续的三个数字或者三个相同的数字可以组成一个tripe,每个数字只能属于一个tripe,问最多能组成多少个tripe ?
https://codeforces.com/problemset/problem/1110/D
【思路】
因为出三个顺子和出三个刻子对答案的贡献是一样的,因此不妨假设以每种牌为开头的顺子都不超过2个,那么就可以做费用提前计算的dp了
dp[i+1][k][l]=max(dp[i][j][k]+(num[i]-j-k-p)/3+p,dp[i+1][k][l]);
dp[下一个值][当前值挪出来凑以上一个值开头的顺子的个数][当前值挪出来凑当前值开头的顺子的个数]
j k l就分别是挪出来凑上上个值 上个值 和当前值的个数
为了避免重复统计,只把这些顺子统计在那个开头的值里面,所以加上l
而最后dp[m+1][0][0]显然对于i=m而言,i-1和i都凑不出顺子了(因为没有i+1),所以均0是最优的
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 5;
int cnt[maxn];
int dp[maxn][3][3];
/*
dp[i][j][k]表示为i的数字中,属于组成三个连续数字的tripe的开头的有k个,属于组成三个连续数字的tripe的中间的有j个
*/
int main() {
int n, m;
while (~scanf("%d%d", &n, &m)) {
memset(dp, 0, sizeof(dp));
memset(cnt, 0, sizeof(cnt));
for (int i = 1; i <= n; i++) {
int x;
scanf("%d", &x);
cnt[x]++;
}
for (int i = 0; i <= m; i++) {
for (int j = 0; j < 3; j++) {//靠前
for (int k = 0; k < 3; k++) {//中间
for (int p = 0; p < 3&& cnt[i + 1] - j - k - p>=0; p++) {//靠后
dp[i + 1][k][p] = max(dp[i + 1][k][p], dp[i][j][k] + (cnt[i + 1] - j - k - p) / 3 + p);
}
}
}
}
printf("%d\n", dp[m+1][0][0]);
}
}
/*
10 6
2 3 3 3 4 4 4 5 5 6
12 6
1 5 3 3 3 4 3 5 3 2 3 3
13 5
1 1 5 1 2 3 3 2 4 2 3 4 5
*/
/*
写代码时请注意:
1.ll?数组大小,边界?数据范围?
2.精度?
3.特判?
4.至少做一些
思考提醒:
1.最大值最小->二分?
2.可以贪心么?不行dp可以么
3.可以优化么
4.维护区间用什么数据结构?
5.统计方案是用dp?模了么?
6.逆向思维?
*/