Codeforces Round #260 (Div. 1) A.Boredom

今天是下定决心好好练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;
}


猜你喜欢

转载自blog.csdn.net/wookaikaiko/article/details/86411283