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