题意
YJQ 上完第10周的程序设计思维与实践后,想到一个绝妙的主意,他对拿数问题做了一点小修改,使得这道题变成了 拿数问题 II。
给一个序列,里边有 n 个数,每一步能拿走一个数,比如拿第 i 个数, Ai = x,得到相应的分数 x,但拿掉这个 Ai 后,x+1 和 x-1 (如果有 Aj = x+1 或 Aj = x-1 存在) 就会变得不可拿(但是有 Aj = x 的话可以继续拿这个 x)。求最大分数。
Input
第一行包含一个整数 n (1 ≤ n ≤ 105),表示数字里的元素的个数
第二行包含n个整数a1, a2, …, an (1 ≤ ai ≤ 105)
Output
输出一个整数:n你能得到最大分值。
Example
Input
2
1 2
Output
2
Input
3
1 2 3
Output
4
Input
9
1 2 1 3 2 2 2 2 3
Output
10
思想
由题意:给一个序列,里边有 n 个数,每一步能拿走一个数,比如拿第 i 个数, Ai = x,得到相应的分数 x,但拿掉这个 Ai 后,x+1 和 x-1 (如果有 Aj = x+1 或 Aj = x-1 存在) 就会变得不可拿(但是有 Aj = x 的话可以继续拿这个 x)
- 首先a[i]存放序列,time[i]统计每个数出现的次数,dp[i]存放以a[i]结尾的序列最大和
- 又有比如:拿了2,则序列中的所有2都可拿,但所有1和3都不可拿,由此想到将序列排序便于排除 x+1 和 x-1 ;
- 因为dp[i]存放以a[i]结尾的序列最大和,且相邻数不可取,得到状态转移方程:dp[i]=max(dp[i-2]+add,dp[i-1]) ,其中add=i* time[i](每次将相同的数全部加上)
代码
#include <iostream>
#include <string>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <stdio.h>
using namespace std;
long long int n;
long long int a[100500],time[100500],dp[100500];
//a[i]存放序列,time[i]统计每个数出现的次数,dp[i]存放以a[i]结尾的序列最大和
int main()
{
cin>>n;
memset(a,0,sizeof(a));
memset(time,0,sizeof(time));
memset(dp,0,sizeof(dp));
for(long long int i=0;i<n;i++)
{
cin>>a[i];
time[a[i]]++;
}
sort(a,a+n); //排序便于排除 x+1 和 x-1
long long int mn=a[0];
long long int mx=a[n-1];
for(long long int i=mn;i<=mx;i++)
{
long long int add=i* time[i]; //每次将相同的数全部加上
dp[i]=max(dp[i-2]+add,dp[i-1]);
}
cout<<dp[mx]<<endl;
return 0;
}