本文搬运自本人初中博客园博客,若图片加载不出来,可到原文查看:https://www.cnblogs.com/zhangtingxi/p/16195790.html

题目链接

题目

Alex doesn't like boredom. That's why whenever he gets bored, he comes up with games. One long winter evening he came up with a game and decided to play it.

Given a sequence $ a $ consisting of $ n $ integers. The player can make several steps. In a single step he can choose an element of the sequence (let's denote it $ a_{k} $ ) and delete it, at that all elements equal to $ a_{k}+1 $ and $ a_{k}-1 $ also must be deleted from the sequence. That step brings $ a_{k} $ points to the player.

Alex is a perfectionist, so he decided to get as many points as possible. Help him.

力克斯不喜欢无聊。

所以每当他感到无聊他就会想出一些游戏。一个冬天的晚上他想出了一个游戏并且决定开始玩这个游戏。

给定一个有n个元素的序列a。你可以做若干次操作。在一次操作中我们可以取出一个数(假设他为x)并删除它,同时删除所有的序列中值为x+1和x-1的数。这一步操作会给玩家加上x分。

输入输出格式

输入格式 第一行一个整数n(1<=n<=10^5),说明这个序列有多少数。 第二行n个整数,分别表示a1,a2……an。

输出格式 一个整数,表示玩家最多能获得多少分

说明: 对于样例4,第一步我们取2,序列变为[2,2,2,2]。接下来每一步都取2,最后获得10分。

思路

先排序离散化,构成一个有序序列 bb,每个数出现 cic_i 次。

由于一个数选了,左右都不能选。而再选这个数,对其它数没影响。所以一种数要么全选,要么全不选。

对于第 ii 个数,全选的贡献为 bi×cib_i\times c_i

那我们就可以dp了。每个数有选和不选两种选择,不选直接从前面两种情况选最大值转移。

选和话需要分类讨论。如果前面的书刚好比这个数小1,只能从前面不选的情况转移过来。否则就可以从前面较大值的情况转移过来。

dp是 O(n)O(n) 的,加上排序总复杂度为 O(nlogn)O(n\log n)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define N 100010
//#define M
//#define mo
int n, m, i, j, k, T;
int a[N], b[N], c[N], dp[N][2];

signed main()
{
// freopen("tiaoshi.in","r",stdin);
// freopen("tiaoshi.out","w",stdout);
n=read();
for(i=1; i<=n; ++i) b[i]=read();
sort(b+1, b+n+1);
for(i=2, a[c[1]=j=1]=b[1]; i<=n; ++i)
{
if(b[i]!=a[j]) ++j;
a[j]=b[i]; ++c[j];
}
a[0]=-99999999;
for(i=1; i<=j; ++i)
{
dp[i][0]=max(dp[i-1][0], dp[i-1][1]);
dp[i][1]=a[i]*c[i]+max(dp[i-1][0], (a[i]-a[i-1]!=1 ? dp[i-1][1] : 1ll-1));
// printf("%lld %lld\n", dp[i][0], dp[i][1]);
}
printf("%lld", max(dp[j][0], dp[j][1]));
return 0;
}

总结

这是一类挺经典的dp题目。

题目解题关键在于每个数要么全选,要么全不选。剩下的就是dp模板了。

这类题目注意分析性质。同时分析出选与不选可以考虑dp。