SDU_week10_C - 拿数问题 II(动态规划)

题目描述

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[i1],pd[i2]+inumi)

当只有一种数字的时候要特殊处理。

注意

在一切大数相乘和大数相加的地方考虑会不会爆int!

代码

#define _ ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define ll long long
#include 
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;

}

你可能感兴趣的:(程序设计与算法)