SDU程序设计思维Week10-作业 C-拿数问题II

SDU程序设计思维Week10-作业

C-拿数问题II

Description

给 n 个数,每一步能拿走一个数,比如拿第 i 个数, Ai = x,得到相应的分数 x,但拿掉这个 Ai 后,x+1 和 x-1 (如果有 Aj = x+1 或 Aj = x-1 存在) 就会变得不可拿(但是有 Aj = x 的话可以继续拿这个 x)。求最大分数。

第一行包含一个整数 n (1 ≤ n ≤ 105),表示数字里的元素的个数

第二行包含n个整数a1, a2, ..., an (1 ≤ ai ≤ 105)

输出一个整数:你能得到最大分值。

Sample


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

Idea

dp[i]表示A[1…i]能拿到的最大分数
转移方程:dp[i]=max(dp[i-1],dp[i-2]+A[i])

题意:x被使用后,value等于x-1或x+1的元素不可以被用

  • 把x转换成数组的下标,value转换成value=x出现的次数,记录这n个数value的左右边界left、right
  • 初始化dp数组,dp[i]表示value∈[1,i]能拿到的最大分数
  • 记录每个a的前溯元素pre,这个数组的意义是记录当前a的前一个出现次数不等于0的索引
  • 根据需求改变之前的转移方程并扫描
    对a[i]=0的元素忽略
    对于前溯元素索引 != i-1时dp[i-2]+A[i]相当于dp[pre[i]] + a[i] * i
    对于前溯元素索引 = i-1时dp[i-2]+A[i]相当于dp[pre[pre[i]]] + a[i] * i

Summary

这道题是动态规划的应用,转移方程是dp[i]=max(dp[i-1],dp[pre[i]]+a[i]*i / dp[pre[pre[i]]] + a[i] * i),把value转化成数组的下标,对应的值是出现次数。扫描时对于出现次数为0的value直接跳过
时间复杂度为O(n)
注意数据范围(1 ≤ ai ≤ 105)!!要使用long long存储dp!!

Codes

#include <iostream>
#include <algorithm>
using namespace std;

long long n, a[100200] = { 0 }, ans = 0, dp[100200] = {0}, pre[100200] = { 0 };

int main()
{
	int left = 1e5 + 1, right = 0;
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		int x;
		cin >> x;
		a[x]++;
		if (left > x)left = x;
		if (right < x)right = x;

	}

	dp[left-1] = 0;
	dp[left] = a[left]*left;
	pre[left] = 0;
	int index = left;
	for (int i = left + 1; i <= right; i++) {
		if (a[i]) { pre[i] = index; index = i; }
	}


	for (int i = left+1; i <= right; i++) {
		if (a[i] == 0)continue;
		if (pre[i] != i - 1) {
			dp[i] = max(dp[i - 1], dp[pre[i]] + a[i] * i);

		}
		else {
			dp[i] = max(dp[i - 1], dp[pre[pre[i]]] + a[i] * i);

		}
		

	}

	for (int i = left; i <= right; i++)
	{
		ans = ans > dp[i] ? ans : dp[i];
	}

	cout << ans << endl;

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值