今天是下定决心好好练DP的第一天,从1600的DP开始做起。
题目链接:点击打开原题目。
题意:给一个长度位n的整数序列,每次选择一个数字删除,删除这个数的同时比这个数大1和小1的数全部被删掉,每次得到的值就是你当前选择的这个数的价值,问能得到的值的总和最大是多少。
思路:在这个序列里面选数的限制条件就是当你选了a[i],你就不能选择a[i]+1和a[i]-1了,因为已经在你选择a[i]的时候被删除了。同时相同价值的数所面临的情况是相同的,能选就都可以选,不能选就都在之前的某一轮被删除了,所以对于所有的数来说,选择他们的关键就是他们的大小,再深入一点想,一个数a[i]的状态只和a[i]-1和a[i]+1有关,将所有的数按照价值和出现的个数存起来,从小到大排序,如果对第i个数a[i]来说存在a[i]-1,,那这个数一定是第i个数,如果存在a[i]+1,一定是第i+1个数,所以枚举下标就好了。
①. dp[i][0]表示第i个数不选
当前第i个不选,那么i-1选不选都对它没影响,所以取前面最大的。得到状态转移方程:dp[i][0]=max(dp[i-1][1],dp[i-1][0]);
②. dp[i][1]表示第i个数要选
那么第i-1个数如果大小是a[i]-1的话,他就不能选。此时状态转移方程是:dp[i][1]=dp[i-1][0]+a[i].va*a[i].cnt,(a[i].va表示这个数的值,a[i].cnt表示这个数的个数,当前这个数都选了,肯定要加上它的价值)
当第i-1个数不是a[i]-1,那么两个数的选择是相互独立的,此时只需要把当前这个数能贡献的答案加上前面最大的。
dp[i][1]=max(dp[i-1][0],dp[i-1][1])+a[i].va*a[i].cnt;
到这里这道题就算是做完了(要开long long)。
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node
{
int va;
int cn;
bool operator<(const node&aa)const
{
return aa.va>va;
}
} a[100010];
ll dp[100010][3];
int cnt[100010];
int main()
{
int n,num;
scanf("%d",&n);
int maxx=-1;
for(int i=1; i<=n; i++)
{
scanf("%d",&num);
cnt[num]++;
maxx=max(maxx,num);
}
int nn=0;
for(int i=1; i<=maxx; i++)
{
if(cnt[i]!=0)
a[++nn].va=i,a[nn].cn=cnt[i];
}
sort(a+1,a+nn+1);
dp[1][1]=(ll)a[1].va*a[1].cn;
dp[1][0]=0;
for(int i=2;i<=nn;i++)
{
if(a[i].va-a[i-1].va==1)
{
dp[i][0]=max(dp[i-1][1],dp[i-1][0]);
dp[i][1]=dp[i-1][0]+(ll)a[i].va*a[i].cn;
}
else
{
dp[i][0]=max(dp[i-1][1],dp[i-1][0]);
dp[i][1]=max(dp[i-1][0],dp[i-1][1])+(ll)a[i].va*a[i].cn;
}
}
ll ans=max(dp[nn][1],dp[nn][0]);
printf("%lld\n",ans);
return 0;
}