题目链接
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[i−1][j]
- 如果第 i i i个学生进组,我们可以考虑 d p [ x ] [ j − 1 ] , x < = i dp[x][j - 1],x <= i dp[x][j−1],x<=i的情况,这时候可以组一个新队,让学生 x + 1 , x + 2 , . . . . . . i − 1 , i x + 1, x + 2, ......i - 1, i x+1,x+2,......i−1,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][j−1]+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[i−1][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[i−1][j−1]+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;
}