【codeforces 1110D Jongmah 】【思维】【dp】

【题意】

给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.逆向思维?
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值