题目描述
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
Hint
对于第三个样例:先选任何一个值为2的元素,最后数组内剩下4个2。然后4次选择2,最终得到10分。
题目分析
以往的拿数问题是对于给定顺序的序列,拿完数字不能拿两边的;本题是给定一堆数字,拿了数字后不能拿该数字两边的数字(如345拿了4不能拿35,无论原数列有没有35),但是每种数字一旦选定就可以都拿走,于是认为原数列为无序数列,我们先对其排序。
然后观察数据范围,1e5,还可以,开一个桶,桶内记录每个数据的出现的次数,在输入数据的同时还用set记录有多少种不同的数字。
定义dp[];//仅考虑1···i能拿到的最大分数,于是状态转移方程为:
d
p
[
i
]
=
m
a
x
(
d
p
[
i
−
1
]
,
p
d
[
i
−
2
]
+
i
∗
n
u
m
i
)
dp[i]=max(dp[i-1],pd[i-2]+i*num_i)
dp[i]=max(dp[i−1],pd[i−2]+i∗numi)
当只有一种数字的时候要特殊处理。
注意
在一切大数相乘和大数相加的地方考虑会不会爆int!
代码
#define _ ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define ll long long
#include <bits/stdc++.h>
using namespace std;
int arr[100009];//初始数列
ll func[100009];//仅考虑1···i能拿到的最大分数
int main()
{
memset(arr,0,sizeof(arr));
memset(func,0,sizeof(func));
int n;//n个数
set<int> cnt;//不同的数的数量
cin>>n;
int temp;
for(int i=0;i<n;i++)
{
cin>>temp;
cnt.insert(temp);
arr[temp]++;
}
func[0]=0;
func[1]=1*arr[1];
if(cnt.size()==1&&func[1]!=0)
{
cout<<func[1];
return 0;
}
int mx=*cnt.rbegin();
for(int i=2;i<=mx;i++)
func[i]=max(func[i-1],func[i-2]+(ll)i*arr[i]);
//long long类型是C99标准增加的新的类型,不在隐式转换的范畴内,手动(long long)
//上面这句是错的,c++中long long可以强制类型转换,否则ll(i)不会传递给arr[i],问题出在爆int
ll ans=func[1];
for(int i=1;i<=mx;i++)
{
if(func[i]>ans)
ans=func[i];
}
cout<<ans;
return 0;
}