(又又做不出来)
原题:https://codeforces.com/contest/1110/problem/D
题意:给你一堆数字,你的任务是将这些数字分组,规定3个相同的数字或者3个连号(i,i,i 或 i,i+1,i+2)为一组,问:最多可以分成多少组
一开始觉得可以贪。。贪心(也不知道是不是叫做贪心咯),三个一组优先选,然后再考虑连号的情况。感觉我还是太。。菜了,可能是做题不够多。
其实这种程度的贪心应该很容易想到其错误性啊。
3个 i,i+1,i+2 = i,i,i (i+1),(i+1),(i+1) (i+2),(i+2),(i+2) ,这个应该需要很快想到,这样的话就说明 3个及以上的连号 其实是没啥意义的,可以被 取3个相同数字 代替。那么需要区别 取相同 或者 取连号 哪个更优只需要在i,i+1,i+2 都<3的范围内验证其正确性就好了。假如贪心策略是优先取相同数字,只需要人脑遍历不同情况,明显3,2,2 这样的情况就需要取连号,那么假如贪心策略是优先取连号呢,同样3,1,3这种情况并不是最优解,因此贪心行不通(也可能其实是可以贪心但我看不出来)
不能贪心的话还能干嘛,动态规划啊。
动态规划一般需要将所有状态遍历一边,那就来想一下如何表示状态,也就是dp方程组表示啥,怎么表示,怎么转移。
对于一张牌i,需要使用它的地方只有4种,
i-2,i-1,i
i-1,i,i+1
i.i+1.i+2
i,i,i
当然假如前3种都确定了,那么第四种也好说,假如num[i]表示i的数量,那么第四种=(num[n]-i-j-k)/3
那么用dp[n][i][j][k],表示从开始到第一二三种分法分别是i,j,k的数字n最多的分法的数量(表述的不太好)
那么代码就是:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e6+7;
int num[maxn],dp[maxn][3][3][3];
int main(){
int n,m; cin>>n>>m;
for(int i=1;i<=n;i++){
int sto; scanf("%d",&sto);
num[sto]++;
}
for(int i=1;i<=m;i++){
for(int j=0;j<3;j++){
for(int k=0;k<3;k++){
for(int l=0;l<3;l++){
if(j+k+l>num[i]) continue;
for(int m=0;m<3;m++){
dp[i][j][k][l]=max(dp[i][j][k][l],dp[i-1][m][j][k]+l+(num[i]-j-k-l)/3);
}
}
}
}
}
int res;
for(int i=0;i<3;i++) res=max(res,dp[m][i][0][0]);
cout<<res<<endl;
return 0;
}