1. 程式人生 > >Codeforces Round #260 (Div. 1) A.Boredom

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;
}