Codeforces 1133E

这篇博客讨论了Codeforces竞赛中的问题1133E,涉及将n个能力值不同的学生分组,每组内的能力值差不超过5。博主分享了先排序再动态规划求解的思路,描述了状态转移方程,并提供了AC代码。尽管博主对动态规划理解尚浅,但希望通过更多练习加深理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接

http://codeforces.com/contest/1133/problem/E

题意

n n n个学生,第 i i i个学生能力值为 a i ai ai,要把他们分成不超过 k k k个且至少一个不为空的组,要求每个组内学生的能力值相差不超过5,问最多能有多少个学生能加入组。

思路

先排序,这样最后分组之后同一组的人一定是相邻的

d p [ i ] [ j ] dp[i][j] dp[i][j]表示考虑前 i i i个学生,分成 j ​ j​ j组时,学生的最大人数

c n t [ i ] ​ cnt[i]​ cnt[i]表示以第 i ​ i​ i个学生开头的一组最多有几个同学

假设学生编号是 1... n ​ 1...n​ 1...n,那么 d p [ i ] [ j ] ​ dp[i][j]​ dp[i][j]有两种转移情况:

  • 如果第 i ​ i​ i个学生不进组,那么 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] ​ dp[i][j] = dp[i - 1][j]​ dp[i][j]=dp[i1][j]
  • 如果第 i ​ i​ i个学生进组,我们可以考虑 d p [ x ] [ j − 1 ] , x &lt; = i ​ dp[x][j - 1],x &lt;= i​ dp[x][j1]x<=i的情况,这时候可以组一个新队,让学生 x + 1 , x + 2 , . . . . . . i − 1 , i ​ x + 1, x + 2, ......i - 1, i​ x+1,x+2,......i1,i加进去,也就是让 c n t [ x ] ​ cnt[x]​ cnt[x],因此 d p [ i ] [ j ] = d p [ x ] [ j − 1 ] + c n t [ x ] ​ dp[i][j] = dp[x][j - 1] + cnt[x]​ dp[i][j]=dp[x][j1]+cnt[x],其中 x + c n t [ x ] − 1 = i ​ x + cnt[x] - 1 = i​ x+cnt[x]1=i

最后答案就是 d p [ n ] [ k ] ​ dp[n][k]​ dp[n][k]

因此得出转移方程:

  • d p [ i ] [ j ] = m a x ( d p [ i ] [ j ] , d p [ i − 1 ] [ j ] ) dp[i][j] = max(dp[i][j], dp[i - 1][j]) dp[i][j]=max(dp[i][j],dp[i1][j])
  • d p [ i + c n t [ i ] − 1 ] [ j ] = m a x ( d p [ i + c n t [ i ] − 1 ] [ j ] , d p [ i − 1 ] [ j − 1 ] + c n t [ i ] ) dp[i + cnt[i] - 1][j] = max(dp[i + cnt[i] - 1][j], dp[i - 1][j - 1] + cnt[i]) dp[i+cnt[i]1][j]=max(dp[i+cnt[i]1][j],dp[i1][j1]+cnt[i])
  • 边界 d p [ i ] [ 0 ] = d p [ 0 ] [ j ] = 0 dp[i][0] = dp[0][j] = 0 dp[i][0]=dp[0][j]=0

标程和题解是下标从0开始的,我看不太懂,这个方法AC了,但是我还是有点懵

目前我对动态规划的理解还不够深刻,请多多指教

准备等我做了足够多动态规划的题目之后再回过头来看

AC代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#include<string>
#include<sstream>
#include<cctype>
#include<map>
#include<stack>
#include<queue>
#include<list>
#include<cstdlib>
#include<ctime>
using namespace std;
typedef long long ll;
const double PI = atan(1.0)*4;
const int INF = 0x7ffffff;
const ll MOD = 1000000007;
const int maxn = 5010;
ll GCD(ll a, ll b){return b==0?a:GCD(b,a%b);}

int a[maxn];
int cnt[maxn];
int dp[maxn][maxn];

int main()
{
//	freopen("input.txt", "r", stdin);
//  freopen("output.txt", "w", stdout);
	int n, k;
	scanf("%d%d", &n, &k);
	for(int i = 1; i <= n; i++)
		scanf("%d", &a[i]);
	sort(a + 1, a + n + 1);
	for(int i = 1; i <= n; i++)
	{
		while(i + cnt[i] <= n && a[i + cnt[i]] - a[i] <= 5)
			cnt[i]++;
	}
/*
	for(int i = 1; i <= n; i++)
	{
		printf("--%d %d %d\n",i,a[i],cnt[i]);
	}
*/
	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= k; j++)
		{
			dp[i][j] = max(dp[i][j], dp[i - 1][j]);
			dp[i + cnt[i] - 1][j] = max(dp[i + cnt[i] - 1][j], dp[i - 1][j - 1] + cnt[i]);
		}
	}
	printf("%d\n", dp[n][k]);
/*
	for(int i = 0; i <= n; i++)
	{
		for(int j = 0; j <= k; j++)
		{
			printf("%d %d %d\n",i,j,dp[i][j]);
		}
	}
*/
	return 0; 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值