【拿数问题 II】dp

题目

题意
给 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;
}
原创文章 46 获赞 1 访问量 1493

猜你喜欢

转载自blog.csdn.net/weixin_43676449/article/details/105711940