Codeforces Global Round 1 D - Jongmah(dp)

题目

n张牌,m种牌,每个牌上一个号

可以连续打三个(3 3 3),也可以连续打顺子(1 2 3)

问最多能打多少组

思路来源

马老师&&航神

题解

首先如果连续3个都有三个,打三个顺和三个仨是一样的,成组消去

打3个相同的也是足够优的,但不是绝对的,

可以证明(好吧我证明不了),

每个拿出6个来专门用于凑顺,

最多给上上个提供2个,上个提供2个,自己开头提供2个

这样足够了,剩下多的用于打仨就行了

 

dp[i+1][j][k]代表对于值为i的数来说,专门拿出j个去凑以i-1开头的顺子,专门拿出k个去凑以i开头的顺子

dp[i+1][k][l]=max(dp[i][j][k]+(num[i]-j-k-l)/3+l,dp[i+1][k][l]);
dp[下一个值][当前值挪出来凑以上一个值开头的顺子的个数][当前值挪出来凑当前值开头的顺子的个数]
j k l就分别是挪出来凑上上个值 上个值 和当前值的个数
为了避免重复统计,只把这些顺子统计在那个开头的值里面,所以加上l
而最后dp[m+1][0][0]显然对于i=m而言,i-1和i都凑不出顺子了(因为没有i+1),所以均0是最优的

心得

感觉是三步三步滑动的这样一个状态转移dp

其实自己写wa贪心代码的时候也考虑着

如果出现1 3 2,1 2 3,1 2 1,2 2 3,这些连续的三个的情况该怎么搞……

然后还分类讨论贪心来着,

现在看来,暴力让它们一起往后推,是为dp。

代码

滚动数组要清零,不然受i-2那个结果影响。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn=1e6+10; 
int dp[2][3][3];
int num[maxn],n,m;
int main()
{ 
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;++i)
    {
       int x;
	   scanf("%d",&x);
	   num[x]++;	
    }
    for(int i=1;i<=m;++i)
    { 
           for(int k=0;k<3;++k)
			{
				for(int l=0;l<3;++l)
				{
					dp[(i+1)%2][k][l]=0;
				}
			}
    	for(int j=0;j<3;++j)
		{
			for(int k=0;k<3;++k)
			{
				for(int l=0;l<3;++l)
				{
					if(num[i]<j+k+l)continue;
					dp[(i+1)%2][k][l]=max(dp[i%2][j][k]+(num[i]-j-k-l)/3+l,dp[(i+1)%2][k][l]);
				}
			}
		} 
	}
	printf("%d\n",dp[(m+1)%2][0][0]); 
	return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值