题记
先分析题干,显然如果分数不在一个等差数列上的分数是互不影响的,比如K=2,那么分数1,3,5,7,9和2,4,6,8是互不影响的,因为这两组任意两个都没法匹配,所以我们大致的思路就是按照给的K先划分一个组,考虑每个组的最大人数,然后再相加。
在每一个组内怎么求最多有多少个人不匹配呢,如果一下子全部都考虑规模太大了,我们试着缩小规模,最后一个人比较有代表性,那么算上他所在的分数之后怎么算呢。仔细揣摩也就两种情况,加上他的这个分数或者不加他的这个分数,这就有点动态规划的内味儿了。我们在考虑一下组内的特点,相邻的两个分数是不能同时取的,比如K=2,那么这一组的每个分数就是2,4,6,8,10,12…之间选,显然分数2,4这样相邻的是不能选的,所以,针对最后一个人来说,如果加上他的这个分数,就不能选他前面的那个分数,而在他上上一个分数的基础上再加上他的分数对应的人数后就是这个组最多不能同时匹配的人数了,即
dp[last]=max(dp[last-1],dp[last-2]+goal[last]),
我们再推广一下一般情况(也就是往前推就能得到递推公式了)
i=0时
dp[i]=goal[i]
i=1时
dp[1]=max(dp[0],goal[1])
i>1时
dp[i]=max(dp[i-1],dp[i-2]+goal[i])
动态规划的题如果感觉不好想那么就从最后一个开始想,逆向思考,毕竟动态规划是递归的另一种
#include <iostream>
#include <map>
using namespace std;
const int Maxn=1e5+10;
int goal[Maxn];//记录每个分数的人数
int dp[Maxn];//前i个人(包括第i个人)中最多有多少人都不匹配
int tmp[Maxn];//记录某一组中每个梯度的人数
int main()
{
int N,K,Ai;
int count=0;//记录不同分数的个数(如果K==0直接输出count)
int ans=0;
cin>>N>>K;
for(int i=0;i<N;i++){
cin>>Ai;
if(goal[Ai]==0)
count++;
goal[Ai]++;
}
if(K==0){
cout<<count<<endl;
}
else{
//把所有分数分成不同组
for(int i=0;i<K;i++){
int pos=0;//某一个组的组内下标
for(int j=i;j<Maxn;j+=K){
tmp[pos++]=goal[j];
}
for(int j=0;j<pos;j++){
if(j==0)
dp[0]=tmp[0];//每一组第一个的dp就是第一个分数的人数
else if(j==1)
dp[1]=max(dp[0],tmp[1]);//每一组第二个的dp就是前两个的最大值
else
dp[j]=max(dp[j-1],dp[j-2]+tmp[j]);//第i个的dp要么等于它上一个分数的dp,要么是上上一个 分数的dp加上该分数的人数
}
ans+=dp[pos-1];
}
cout<<ans<<endl;
}
return 0;
}