题目
题意
给 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分。
题目大意
本题给出了一个数字序列,按要求从其中选取若干个数字,使选取结果和最大。选取规则是如果选取了数字x,则其他数值为x的也可以选,但是对应的如果存在x-1与x+1则都不能选择。
解题思路
本题是一道dp题,原因是要根据不同的选数方案得到最优解,其中有很明显的动态规划思路。首先我们将输入的数字序列进行处理,重复数字只记录一次,在结构体中存入对应数值和出现次数,这样按照数字从小到大排序,我们便有了以下转移方程:
dp[i] = dp[i-1] + a[i].num * a[i].count;if(a[i].num-1 != a[i-1].num)
dp[i] = max(dp[i-1],dp[i-2]+a[i].num*a[i].count);else
初始化:
dp[0] = 0;
dp[1] = a[1].num * a[1].count;
遍历过程只需要遍历数字的种类数而不是总个数,最后输出遍历过程中的最大dp值即可。
具体代码
#include<iostream>
#include<sstream>
#include<algorithm>
#include<string>
#include<cstring>
#include<iomanip>
#include<vector>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<map>
#define MAXN 100005
#define ll long long
using namespace std;
int vis[100005];
ll dp[100005];
struct node{
ll num;
ll count;
}a[100005];
bool cmp(node a, node b)
{
return a.num < b.num;
}
int main()
{
memset(vis,0,sizeof(vis));
ll n,tmp,cnt = 1;
cin >> n;
for(int i = 0; i < n; i++)
{
cin >> tmp;
if(!vis[tmp])
{
a[cnt].num = tmp;
a[cnt].count = 1;
vis[tmp] = cnt;
cnt++;
}
else
{
a[vis[tmp]].count++;
}
}
sort(a+1,a+cnt,cmp);
dp[0] = 0;
dp[1] = a[1].num * a[1].count;
ll maxn = dp[1];
for(int i = 2; i < cnt; i++)
{
if(a[i].num-1 != a[i-1].num)
{
dp[i] = dp[i-1] + a[i].num * a[i].count;
}
else
{
dp[i] = max(dp[i-1],dp[i-2]+a[i].num*a[i].count);
}
maxn = max(maxn,dp[i]);
}
cout << maxn << endl;
return 0;
}