最近连着碰见分组动态规划的题,所以我把这两道收集下来,分析一下。
先看第一道,
题意如下。题目链接在此**************+
给定 n 个整数 a1,a2,…,an。
现在,请你从中挑选一些数,并将选出的数进行分组。
要求:
选出的数最多划分为 k 组(至少 1 组)。
同一组内,任意两数之差的绝对值不超过 5。
所选出的数尽可能多。
请问,最多可以选出多少个数进行分组?
输入格式
第一行包含两个整数 n 和 k。
第二行包含 n 个整数 a1,a2,…,an。
输出格式
输出一个整数,表示可以选出的最大整数数量。
数据范围
1≤k≤n≤5000,
1≤ai≤109
输入样例1:
5 2
1 2 15 15 15
输出样例1:
5
解析:
因为同组内,任意两数之差的绝对值不超过 5,所以得先排序。一看让求最大值,很容易想到动态规划,状态表示并不难想,f[i][j] 表示前i 个数分了 j 组的最大整数的数量。然后看状态转移,集合分为选第i个数和不选第i个数,而选第i个数,得知道当前组的整数数量,加上f[i-m][j-1] 。当时就是这一直想不起来,我一直以为还得有第三次遍历找到同组的前一个数,然后一直卡到这,其实一想也不难,因为排序过了,所以绝对值不超过5的同组数是连续的,用一个指针找到这一组第一个数就能转移了。
本质上也就是个单指针加DP。
代码如下
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5010;
int a[N];
int f[N][N];
int main()
{
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);
//滑动数组,取从大到小排前k组之和,还得判重,这样的话就太麻烦了,还是得看yxc直接双指针加DP
for (int i=1,j=1;i<=n;i++){
while (a[i]-a[j]>5) j++;
for (int u =1;u<=k;u++){
f[i][u] = max(f[i-1][u],f[j-1][u-1]+i-j+1);
}
}
cout<<f[n][k];
return 0;
}
第二道是我们学校ACM校赛的题,真是当时我们小组真是拉了,差分看成线段树没做,这道DP也没弄出来,我还是感觉得三维才能做,但是空间超了,主要还是题没看全,要是看见那些歌曲是连续的且每组都是m,我绝逼能做出来,博弈论没做出来最后18名,以后得努力,不能犯这种会做但紧张做不出来的错。
题目链接在此+++++++++++++++++++++++++++++++++
题意如下:
vivy是一台自律人形Ai,为了达成自己被赋予的“透过歌唱让大家幸福”这一使命,vivy不论何时都全心歌唱,她的目标是站到主舞台之上。
现在给出 n 首歌曲的受欢迎程度 a1,a2,a3· · ·an 。现在vivy需要选出k组歌曲:
[l1,r1],[l2,r2]· · · · · ·[lk,rk] (1<=l1<=r1<l2<=r2· · ·<lk<=rk<=n; ri-li+1=m);
使得选出的k组歌曲受欢迎程度之和是所有可能里最大的。
输入描述:
第一行输入3个整数 n,m,k。分别表示歌曲的数量n,每组歌曲的数量m,需要选k组歌曲。中间用空格隔开。
第二行n个整数,a1,a2· · · an。表示每个歌曲的受欢迎程度。
数据规范:
1<=(m×k)<=n<=5000。
0<=ai<=109
输出描述:
输出一个整数,表示最大的受欢迎程度和。
示例1
输入
复制
5 2 1
1 2 3 4 5
输出
复制
9
这道题得开二维longlong数组,在内存限制下二维最多开5700,得提前将时间空间复杂度计算好,不然就都是无用功,直接上代码。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,m,k,f[5007][5007],sum[5007];
int main(){
ll x;
cin>>n>>m>>k;
for(int i=1;i<=n;++i){
cin>>x; sum[i]=sum[i-1]+x;
}
ll ans=0;
for(int i=m;i<=n;++i){
for(int j=1;j<=k;++j){
f[i][j]=max(max(f[i][j],f[i-1][j]),f[i-m][j-1]+sum[i]-
sum[i-m]);
ans=max(ans,f[i][j]);
}
}
cout<<ans<<"\n";
return 0;
}
通过上面两道题,得出了这种题的规律,状态转移是从当前组的开头转移加上到当前元素的值。
多做题多积累,得先从那些竞赛的签到题开始,要在最短的时间做出来,然后练习中等题,挑战难题。