CF Codeforces Global Round 1 D. Jongmah /*三元对dp*/
dp,有难度。。。
发现,如果同样的数字很多,一定先取那些一样的配对,所以先计算出每个数有几个,所以可以减少dp的范围。
因为3个[i,i+1,i+2]可以转换为[i,i,i],[i+1,i+1,i+1],[i+2,i+2,i+2]。dp的时候对于一个三元对[i,i+1,i+2]只需要转移他是0个,1个,2个的情况这样就可以减少dp范围。
因为对于一个大小为i的数,包含他的三元有序对为[i-2,i-1,i],[i-1,i,i+],[i,i+1,i+2],大小为i-1的数,包含他的三元有序对为[i-3,i-2,i-1],[i-2,i-1,i],[i-1,i,i+1]。发现两项的有两个三元有序对重合,就可以记录,有了上述的范围,就可以dp了。
然后中途加上去掉选取连号之后剩下的相同号的情况。
注意边界:选取对数,不能超过个数。不注意这个,中途可能会出错。
/*
Date: 10/02/19 16:44
Description:
*/
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
int dp[1000005][3][3];
int a[1000005];/*每个数出现的次数*/
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
int a_i;
scanf("%d",&a_i);
a[a_i]++;
}
for(int i=1;i<=m;i++){
for(int j=0;j<=2;j++){
for(int k=0;k<=2;k++){
for(int z=0;z<=2;z++){
if(z+k+j>a[i]) continue;/*边界处理,很简洁*/
dp[i][j][k]=max(dp[i][j][k],
dp[i-1][z][j]+k+(a[i]-j-k-z)/3);
/*cout<<"dp"<<i<<"|"<<j<<"|"<<k<<' '<<dp[i][j][k]<<endl;*/
}
}
}
}
cout<<dp[m][0][0];
return 0;
}