题目大意:
给定一个含有n个整数的数组,你可以进行多次操作,每次操作从数组选一个数a[k],然后将其删除,然后删除与a[k]-1和a[k]+1相等的数,然后可以得到a[k]分,求进行多次操作后得到的最多的分。
题解:
设dp[i]表示当前删除的最大数字为i时得到的最大得分
dp[i]=max(dp[i-1],dp[i-2]+i*num[i]); num[i]是数字i出现的次数
对于当前值为i的数,只有选和不选两种情况
dp[i-1]是不选第i个
dp[i-2]+cnt[i]*i 选第i个这里大家可能会有疑问,题中说删了ak的话,ak-1和ak+1也都会被删掉,但是这个状态转移方程却只考虑了ak-1,那么ak+1呢?也就是说当前的选择会对后来造成影响。
如果选了第i个数,那么其实就默认,第i+1个数不能选了;
而取第i+1个数,也是在默认第i个数没有选。
这就代表了2种可能的情况,选第i+1个和不选第i个(也就是转移方程中的dp[i-1]相对应),就像我们选第i个的时候和dp[i-2]相对应,我们选第i+1个的时候是和dp[i-1]对应的。
既然相对应包含在了转移方程里,那么我们就不同再多余考虑了。
dp只是一种优雅的暴力枚举方式,在计算的时候取最大值,如果取第i个数是能到最大值的方法的话,取第i+1个数得到的值一定比取第i个数小。
至于到底选哪种,让ans=max(ans,dp[i]);这句话来选择吧
#include<bits/stdc++.h>
#include<cstring>
using namespace std;
#define ll long long
ll a[100010];
ll num[100010];
ll dp[100010];
int main()
{
int n;
scanf("%d",&n);
ll maxx=0;
for(int i=1;i<=n;++i)
{
scanf("%I64d",&a[i]);
++num[a[i]];
maxx=max(maxx,a[i]);
}
ll ans=0;
//dp[i]表示当前删除的最大数字为i时得到的最大得分
dp[0]=0;dp[1]=num[1];
for(int i=2;i<=maxx;++i)
{
dp[i]=max(dp[i-1],dp[i-2]+i*num[i]);
//dp[i-1]是不选第i个
//dp[i-2]+cnt[i]*i 选第i个
ans=max(ans,dp[i]);
}
printf("%I64d\n",ans);
return 0;
}