每日一题·夏季 [AcWing 3583] 整数分组

题目

AcWing 3583 整数分组
给定 n 个整数 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,,an
现在,请你从中挑选一些数,并将选出的数进行分组。

要求:

  1. 选出的数最多划分为 k 组(至少 1 组)。
  2. 同一组内,任意两数之差的绝对值不超过 5。
  3. 所选出的数尽可能多。

请问,最多可以选出多少个数进行分组?
 

输入格式

第一行包含两个整数 n 和 k。
第二行包含 n 个整数 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,,an

输出格式

输出一个整数,表示可以选出的最大整数数量。
 

数据范围

1 ≤ k ≤ n ≤ 5000 1≤k≤n≤5000 1kn5000,
1 ≤ a i ≤ 1 0 9 1≤a_i≤10^9 1ai109
 

输入样例1:

5 2
1 2 15 15 15

输出样例1:

5

输入样例2:

6 1
36 4 1 25 9 16

输出样例2:

2

输入样例3:

4 4
1 10 100 1000

输出样例3:

4

思路

首先对于全部数可以进行排序。
首先我们看看最优解具有什么性质
能够用dp需要是一个序列,而这里是一个集合,看看如何在这里把它变成一个序列

可以发现最优解可以具有如下的性质:

  1. 对于两个数,如果满足条件(差小于5),那么中间的数一定都可以加入到这个组里(即可选也可以不选),因为每一组是否满足条件只是看最大值与最小值是否满足要求,与中间的值没有关系,那么为了最优解肯定都是都加入到解里面。
  2. 最优解中会不会有区间重合?最优解中可以有区间重合,但是比如两个组有区间重合,可以分成两个没有区间重合的组,仍满足最优解,所以最优解可以没有区间重合。
  3. 对于最后一段,最优解中,这一组可以尽可能的长。(这个性质主要是为了用于状态转移)
    在这里插入图片描述
    即蓝色的是本来的最优解,我们调整这选的两个组,把前面的一组中的满足最后面一组的部分分一部分去,这样仍然满足最优解,且最后一组尽可能长了。选的元素个数也没有变。

所以最优解我们可以在这个集合的满足以上性质的子集里面去寻找,最优解在这个子集中一定存在,而满足以上性质的情况下就可以用dp来求解。在这里插入图片描述
这样子选就是一段一段的选,那么就是一个个序列了,所以就可以用dp来做了。

我们可以用 f ( i , j ) f(i,j) f(i,j)来进行状态表示,它表示的集合是从 1 − i 1-i 1i 中选取 j j j 组的所有方案的集合,所存的数表示的属性是这个集合中的方案中的最大的元素个数。
对于状态计算,我们对于集合进行划分。
分为在 1 − i 1-i 1i 中选取第 i i i 个和不选取 i i i 个两个子集。
对于不选取第 i i i 个的情况,那么这个集合所取的属性(即最大数)就是 f [ i − 1 ] [ j ] f[i-1][j] f[i1][j]
对于选取第 i i i 个的情况,根据上面的性质,那么最后一个区间可以尽可能的大。在这里插入图片描述
肯定都得选最后一组,那么最大值就是看前面。
所以最大值就是 f [ k − 1 ] [ j − 1 ] + ( i − k + 1 ) f[k - 1][j - 1] + (i - k + 1) f[k1][j1]+(ik+1)
这里求得 k k k 可以用双指针扫描。
然后取两种情况的最大值即可。

然后最终答案是 f [ n ] [ m ] f[n][m] f[n][m]的值,这里分成 m m m 组是否是最优的呢?这里 m < n m<n m<n,可以是一种方案,最优解可以是分成 m m m 组,也可以不是 m m m 组,对于不是 m m m 组的最优解,实际上可以化成 m m m 组的情况,把组分开成多组就可以了。

代码

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 5010;

int n, m;
int w[N];
int f[N][N];

int main()
{
    
    
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
    sort(w + 1, w + n + 1);

    for (int i = 1, k = 1; i <= n; i ++ )
    {
    
    
        while (w[i] - w[k] > 5) k ++ ;
        for (int j = 1; j <= m; j ++ )
            f[i][j] = max(f[i - 1][j], f[k - 1][j - 1] + (i - k + 1));
    }

    printf("%d\n", f[n][m]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xxmy7/article/details/117392551