DP简单分组动态规划例题

最近连着碰见分组动态规划的题,所以我把这两道收集下来,分析一下。
先看第一道,
题意如下。题目链接在此**************+
给定 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;
}

通过上面两道题,得出了这种题的规律,状态转移是从当前组的开头转移加上到当前元素的值。
多做题多积累,得先从那些竞赛的签到题开始,要在最短的时间做出来,然后练习中等题,挑战难题。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值