贪心+DP
先将数组排序,然后分析合理选取方案的性质:
- 选取的组应该是一段连续的区间
- 选取的两个相邻的区间可以相交也可以不相交(相交不重复计点数)
- 对于最后一组来说,可以尽可能的向左延伸。
证明第3点:
假如最优解中最后一组没有延伸到最远,说明本该可以延伸到的点要么属于另一个组要么没有被选中:
1.如果这些点属于前一个区间,那么根据性质2可以将这些点放到最后一组;
2.如果这些点没有被选,那么将这些点放到最后一组,答案会更优。
因此可以将最优解方案转换成我们的目标方案,且答案不会变差。
状态表示:$f[i][j]$表示从前$i$个数中选,共$j$组的最大点数
状态转移:$f[i][j] = max(f[i - 1][j] , f[k - 1][j - 1] + (i - k + 1))$
(根据第三点性质,可以得出应向第$i$个点最左能到的点转移而来)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5010;
int f[N][N] , a[N];
int n , k;
int main()
{
cin >> n >> k;
for (int i = 1; i <= n; i ++ ) cin >> a[i];
sort(a + 1 , a + n + 1);
for(int i = 1 , k = 1 ; i <= n ; i++)
{
while(a[i] - a[k] > 5) k++;//找到对于i来说,最左能到的点
for(int j = 1 ; j <= k ; j++)
f[i][j] = max(f[i - 1][j] , f[k - 1][j - 1] + (i - k + 1));
//根据第三点性质,可以得出应向第i个点最左能到的点转移而来
}
cout << f[n][k] << endl;
}