合唱团_牛客网【动态规划实现】

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

有 n 个学生站成一排,每个学生有一个能力值,牛牛想从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 k 个学生的能力值的乘积最大,你能返回最大的乘积吗?

输入描述:

每个输入包含 1 个测试用例。每个测试数据的第一行包含一个整数 n (1 <= n <= 50),表示学生的个数,接下来的一行,包含 n 个整数,按顺序表示每个学生的能力值 ai(-50 <= ai <= 50)。接下来的一行包含两个整数,k 和 d (1 <= k <= 10, 1 <= d <= 50)。

输出描述:

输出一行表示最大的乘积。

示例1

输入

3
7 4 7
2 50

输出

49

 

分析:首先这是一个最优化问题【因为选择的k个学生不同,最后的结果也不一样,题目要求求最大,就需要从这些结构中选出最大的那个】

所以我们可以先确定 选择第k个学生的所有可能位置,得到这些所有可能位置下的最大乘积,然后从这些最大乘积中再选出最大的那个

那么如何确定第k个学生的所有可能位置呢?

我们只要保证 从前往后选择时,至少有k-1个学生可选,那么剩余的位置就是第k个学生的所有可能位置了

如何求解:计算出选择k-1个人的最大乘积,然后乘以当前第k个学生的能力值,即可得到选择k个学生的最大乘积

那么如何确定第k-1个学生的所有可能位置呢?

这里有两点需要注意:

1.保证 从前往右选择时,至少有k-2个学生可选

2.第k-1个学生跟第k个学生位置编号差不超过d

所以在找第k-1个学生所有可能位置的初始值时,我们需要取的是上述两种情况下的较大值,这样两种情况才能同时满足

此时又有一个问题值得注意:学生的能力值有正有负,那么

在第k个学生能力值为正数的情况下,求选择k个学生的最大乘积时,直接用已得到的选择k-1个学生的最大乘积乘以第k个学生的能力值便可得到

在第k个学生能力值为负数的情况下,求选择k个学生的最大乘积时,我们需要注意的是用k-1个学生的最小乘积乘以第k个学生的能力值,因为负的越少才越大

学生能力数组记为arr[n+1],第i个学生的能力值为arr[i]

用动态规划求解:

定义状态:f[one][k]:从n个人中选择k个人时的最大乘积,其中one表示第k个人【选择的最后一个人】的位置  ,k表示包括这个人,一共有k个人

                 g[one][k]:从n个人中选择k个人时的最小乘积,其中one表示第k个人【选择的最后一个人】的位置,k表示包括这个人,一共有k个人

状态转移方程:f[one][k] = max(f[one][k],max(f[left][k-1]*arr[one],g[left][k-1]*arr[one]))   

              g[one][k] = min(f[one][k],min(f[left][k-1]*arr[one],g[left][k-1]*arr[one]))   

初始状态:f[one][1]=g[one][1]=arr[one]   one从1到n   表示选择1个学生时,当前的最大/最小乘积就是该学生的能力值

返回结果:max(f[one][k])   one从k到n

import java.util.Scanner;

public class Chorus {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()) {
            //总人数
            int n = sc.nextInt();
            //学生能力值数组,第i个人直接对应arr[i]
            int[] arr = new int[n + 1];
            //初始化
            for (int i = 1; i <= n; i++) {//人直接对应坐标
                arr[i] = sc.nextInt();
            }
            //选择的学生数
            int kk = sc.nextInt();
            //间距
            int dd = sc.nextInt();

            output(n,arr,kk,dd);
        }
    }

    private static void output(int n, int[] arr, int kk, int dd) {
        /*
         * 递推的时候,以f[one][k]的形式表示
         * 其中:one表示最后一个人的位置,k为包括这个人,一共有k个人
         * 原问题和子问题的关系:f[one][k]=max{f[left][k-1]*arr[one],g[left][k-1]*arr[one]}
         */
        //规划数组
        long[][] f = new long[n + 1][kk + 1];//人直接对应坐标,n和kk都要+1
        long[][] g = new long[n + 1][kk + 1];
        //初始化k=1的情况
        for (int one = 1; one <= n; one++) {
            f[one][1] = arr[one];
            g[one][1] = arr[one];
        }
        //自底向上递推
        for (int k = 2; k <= kk; k++) {
            for (int one = k; one <= n; one++) {   //选k个人时,第k个人所有可能的位置
                f[one][k] = Long.MIN_VALUE;
                g[one][k] = Long.MAX_VALUE;
                for (int left = Math.max(k - 1, one - dd); left <= one - 1; left++) {  //第k-1个人的所有可能位置【既要保证间隔满足条件,又要保证第k-1个人前面还有k-2个人可选】
                    f[one][k] = Math.max(f[one][k], Math.max(f[left][k - 1] * arr[one], g[left][k - 1] * arr[one]));
                    g[one][k] = Math.min(g[one][k], Math.min(f[left][k - 1] * arr[one], g[left][k - 1] * arr[one]));
                }
                //此时第三个for循环遍历完,就能确定 当第k个人位置为one【one从k遍历到n】,且共选择k个人【k从2遍历到kk】时 的最大/最小乘积
                //当下次需要确定 选择k+1个人的最大乘积 时,只需要 先确定第k+1个人的所有可能位置(也用one表示,one从k+1遍历到n),
                // 然后 在确定好第k-2个人的所有可能位置后,通过计算 这些位置的最大/最小乘积 与 arr[one]的乘积,确定选择k+1个人,位置为one时的最大/最小乘积
            }
        }
        /*
        * kk=3 dd=2
        * arr={0, -10, -20, -30, -40};
        * f[1][1]=-10、f[2][1]=-20、f[3][1]=-30、f[4][1]=-40
        * g[1][1]=-10、g[2][1]=-20、g[3][1]=-30、g[4][1]=-40
        *
        * k=2:
        *   one: 2~4
        *    one:2
        *     f[2][2]=Long.MIN_VALUE
        *     g[2][2]=Long.MAX_VALUE
        *     left: 1~1
        *       left:1
        *         f[2][2]=max(Long.MIN_VALUE,max(f[1][1]*arr[2],g[1][1]*arr[2]))=max(Long.MIN_VALUE,max(-10*-20,-10*-20))=max(Long.MIN_VALUE,200)=200
        *         g[2][2]=min(Long.MAX_VALUE,min(g[1][1]*arr[2],g[1][1]*arr[2]))=min(Long.MAX_VALUE,min(-10*-20,-10*-20))=min(Long.MIN_VALUE,200)=200
        *    one:3
        *     f[3][2]=Long.MIN_VALUE
        *     g[3][2]=Long.MAX_VALUE
        *     left:1~2
        *       left:1
        *         f[3][2]=max(Long.MIN_VALUE,max(f[1][1]*arr[3],g[1][1]*arr[3]))=max(Long.MIN_VALUE,max(-10*-30,-10*-30))=300
        *         g[3][2]=min(Long.MAX_VALUE,min(f[1][1]*arr[3],g[1][1]*arr[3]))=min(Long.MAX_VALUE,min(-10*-30,-10*-30))=300
        *       left:2
        *         f[3][2]=max(f[3][2],max(f[2][1]*arr[3],g[2][1]*arr[3]))=max(300,max(-20*-30,-20*-30))=600
        *         g[3][2]=min(g[3][2],min(f[2][1]*arr[3],g[2][1]*arr[3]))=min(300,min(-20*-30,-20*-30))=600
        *    one:4
        *      f[4][2]=Long.MIN_VALUE
        *      g[4][2]=Long.MAX_VALUE
        *      left: 2~3
        *        left:2
        *          f[4][2]=max(Long.MIN_VALUE,max(f[2][1]*arr[4],g[2][1]*arr[4]))=max(Long.MIN_VALUE,max(-20*-40,-20*-40))=800
        *          g[4][2]=min(Long.MAX_VALUE,min(f[2][1]*arr[4],g[2][1]*arr[4]))=min(Long.MIN_VALUE,min(-20*-40,-20*-40))=800
        *        left:3
        *          f[4][2]=max(f[4][2],max(f[3][1]*arr[4],g[3][1]*arr[4]))=max(800,max(-30*-40,-30*-40))=1200
        *          g[4][2]=min(g[4][2],min(f[3][1]*arr[4],g[3][1]*arr[4]))=min(800,min(-30*-40,-30*-40))=1200
        *    one:5
        *      f[5][2]=Long.MIN_VALUE
        *      g[5][2]=Long.MAX_VALUE
        *      left: 3~4
        *        left:3
        *          f[5][2]=
        *          g[5][2]=
        *        left:4
        *          f[5][2]=
        *          g[5][2]=
        * k=3:
        *   one:3~4
        *    one:3
        *      f[3][3]=Long.MIN_VALUE
        *      g[3][3]=Long.MAX_VALUE
        *      left:2~2
        *        f[3][3]=max(Long.MIN_VALUE,max(f[2][2]*arr[3],g[2][2]*arr[3]))=max(Long.MIN_VALUE,max(200*-30,200*-30))=-6000
        *        g[3][3]=min(Long.MAX_VALUE,min(f[2][2]*arr[3],g[2][2]*arr[3]))=min(Long.MAX_VALUE,min(200*-30,200*-30))=-6000
        *    one:4
        *      f[4][3]=Long.MIN_VALUE
        *      g[4][3]=Long.MAX_VALUE
        *      left:2~3
        *        left:2
        *          f[4][3]=max(Long.MIN_VALUE,max(f[2][2]*arr[4],g[2][2]*arr[4]))=max(Long.MIN_VALUE,max(200*-40,200*-40))=-8000
        *          f[4][3]=min(Long.MAX_VALUE,min(f[2][2]*arr[4],g[2][2]*arr[4]))=min(Long.MAX_VALUE,min(200*-40,200*-40))=-8000
        *        left:3
        *          f[4][3]=max(f[4][3],max(f[3][2]*arr[4],g[3][2]*arr[4]))=max(f[4][3],max(600*-40,600*-40))=-24000
        *          g[4][3]=min(g[4][3],min(f[3][2]*arr[4],g[3][2]*arr[4]))=min(f[4][3],min(600*-40,600*-40))=-24000
        *
        * */

        //当计算出来第k个人所有可能位置的最大乘积后,只需要从这些所有可能位置中挑选出最大的那个最大乘积即可
        long result = Long.MIN_VALUE;
        for (int one = kk; one <= n; one++) {
            if (result < f[one][kk]) {
                result = f[one][kk];
            }
        }
        System.out.println(result);
    }
}

 

合唱团》算法解析(含思路解答示意图)【牛客网编程题——JAVA实现 【考点:动态规划动态规划变形,难度指数上升题目描述有 n 个学生站成一排,每个学生有一个能力,牛牛想从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 k 个学生能力的乘积最大,你能返回最大的乘积吗?输入描述每个输入包含 1 个测试用例。每个测试数据的第一行包含一个整数 n (1 &lt;= n &lt;= 50),表示学生的个数,接下来的一行,包... 阅读详情

相关推荐

牛客网编程题——合唱团(网易2017)

    这题在编程达人面前应该算是水题,但这算是我第一个做出来的动态规划程序,作为纪念,分享一下。题目链接:https://www.nowcoder.com/practice/661c49118ca241909add3a11c96408c8?tpId=85&amp;tqId=29830&amp;tPage=1&amp;rp=1&amp;ru=/ta/2017test&amp;qru=/ta/201...

banana_cjb的博客 2157

牛客小白月赛24题解(B、C)

B题: 链接:组队 来源:牛客网 题目描述 你的团队中有 nnn 个人,每个人有一个能力 aia_iai​,现在需要选择若干个人组成一个团队去参加比赛,由于比赛的规则限制,一个团队里面任意两个人能力的差必须要小于等于 kkk ,为了让更多的人有参加比赛的机会,你最多能选择多少个人参加比赛? 输入描述: 第一行一个整数 TTT,表示案例组数。每个案例有两行:第一行两个正整数 n,kn,kn,k ,表示人的数量。第二行n个以空格分隔的整数 aia_iai​ ,表示每个人的能力。 输出描述: 每个案例输出一

1937

牛客网网易笔试算法练习-合唱团

合唱团 题目描述 有 n 个学生站成一排,每个学生有一个能力,牛牛想从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 k 个学生能力的乘积最大,你能返回最大的乘积吗? 输入描述: 每个输入包含 1 个测试用例。每个测试数据的第一行包含一个整数 n (1 &lt;= n &lt;= 50),表示学生的个数,接下来的一行,包含 n 个整数,按...

justDoIt 397

Codeforces 1133E

题目链接 http://codeforces.com/contest/1133/problem/E 题意 nnn个学生,第iii个学生能力为aiaiai,要把他们分成不超过kkk个且至少一个不为空的组,要求每个组内学生能力相差不超过5,问最多能有多少个学生能加入组。 思路 先排序,这样最后分组之后同一组的人一定是相邻的 设dp[i][j]dp[i][j]dp[i][j]表示考虑前iii个学生...

roMxx 506

Codeforces 626F Group Projects

题目大意 有n个学生,每个学生有一个能力%a_i%

qq_45717538的博客 325

网易校招java_网易17校招编程笔试题Java解法赏析(更新至第9题)

1 合唱团(动态规划)分析要求n个学生中选择k个,使这k个学生能力乘积最大。这是一个最优化的问题。另外,在优化过程中,提出了相邻两个学生的位置编号差不超过d的约束。如果不用递归或者动态规划,问题很难入手,并且,限制条件d也需要对每一个进行约束,编程十分复杂所以,解决的方法是采用动态规划(原因:1.最优化问题2.可分解为最优子结构)分解1.对该问题的分解是关键。从n个学生中,选择k个,可以看成是...

weixin_33291624的博客 313

NOIP模拟赛 鸽巢原理 + 众数 + Trie

Set Set(set.cpp/c/pas, 1s, 64MB) 题目描述 你手上有N个非负整数, 你需要在这些数中找出一个非空子集, 使得它的元素之和能被N整除. 如果有多组合法方案, 输出任意一组即可. 注意: 请使用高效的输入输出方式避免输入输出耗时过大. 输入格式 第一行一个整数N, 代表数字个数. 接下来一行N个数, 代表你手上的数. 输出格式

Le Petit Prince 808

[笔试训练](三十二)094:素数回文095:活动安排096:合唱团

2.当前一个活动的结束时间晚于后一个活动的开始时间,活动重叠,ret不变,同时更新两个活动结束时间的较早(最先结束的活动),好与再后一个活动比较。1.将同一个活动的开始时间和结束时间组成一个pair<LL,LL>,将pair排序后,逐个分情况讨论。3.当前一个活动的结束时间早于后一个活动的开始时间,活动不重叠,ret++。

m0_64476561的博客 471

DP 网易内推:合唱团

链接:https://www.nowcoder.com/questionTerminal/661c49118ca241909add3a11c96408c8来源:牛客网 [编程题]合唱团 热度指数:18491 时间限制:1秒 空间限制:32768K 算法知识视频讲解 有 n 个学生站成一排,每个学生有一个能力,牛牛想从这 n 个学生中按照顺序选取 k ...

weixin_30384031的博客 74

牛客网2017校招真题在线编程之合唱团问题——动态规划问题首秀

牛客网2017校招真题在线编程之合唱团问题——动态规划问题首秀 先贴题目 题目描述 有 n 个学生站成一排,每个学生有一个能力,牛牛想从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 k 个学生能力的乘积最大...

diedingpeng7793的博客 274

合唱团 牛客网动态规划题目

动态规划比较有趣的题型

GD_small_bit的博客 638

牛客网合唱团

题目描述有 n 个学生站成一排,每个学生有一个能力,牛牛想从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 k 个学生能力的乘积最大,你能返回最大的乘积吗?输入描述:每个输入包含 1 个测试用例。每个测试数据的第一行包含一个整数 n (1 &lt;= n &lt;= 50),表示学生的个数,接下来的一行,包含 n 个整数,按顺序表示每个学生能力...

梦想还是要有的 433

牛客网合唱团

题目描述 有 n 个学生站成一排,每个学生有一个能力,牛牛想从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 k 个学生能力的乘积最大,你能返回最大的乘积吗? 输入描述: 每个输入包含 1 个测试用例。每个测试数据的第一行包含一个整数 n (1 i(-50 输出描述: 输出一行表示最大的乘积。 示例1

~★sunshine☆的博客专栏 737

动态规划题目-合唱团-学习笔记

动态规划题目-合唱团 题目地址及我参考的解析 牛客网-[编程题]合唱团 题目 [编程题]合唱团 热度指数:86848 时间限制:1秒 空间限制:32768K 有 n 个学生站成一排,每个学生有一个能力,牛牛想从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 k 个学生能力的乘积最大,你能返回最大的乘积吗? 输入描述: 每个输入包含 1 个测试用...

qq_35362055的博客 282

动态规划合唱团问题解析(二)

牛客网网易的校招编程题 题目:有 n 个学生站成一排,每个学生有一个能力,牛牛想从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 k 个学生能力的乘积最大,你能返回最大的乘积吗?输入:每个输入包含 1 个测试用例。每个测试数据的第一行包含一个整数 n (1 <= n <= 50),表示学生的个数,接下来的一行,包含 n 个整数...

weixin_30553065的博客 177

牛客网2017校招真题--合唱团

                           牛客网2017校招真题--合唱团 题目描述   解题思路 代码 #include&lt;iostream&gt; #include&lt;vector&gt; #include&lt;algorithm&gt;                                                          ...

南方有乔木的博客 352

牛客网 合唱团

题目描述 有 n 个学生站成一排,每个学生有一个能力,牛牛想从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 k 个学生能力的乘积最大,你能返回最大的乘积吗? 输入描述: 每个输入包含 1 个测试用例。每个测试数据的第一行包含一个整数 n (1 &lt;= n &lt;= 50),表示学生的个数,接下来的一行,包含 n 个整数,按顺序表示每...

Dan的专栏 772

[每日一题] 79. 合唱团(动态规划、网易17系列)

1. 题目来源 链接:合唱团 来源:牛客网 2. 题目说明 有 n 个学生站成一排,每个学生有一个能力,牛牛想从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 k 个学生能力的乘积最大,你能返回最大的乘积吗? 输入描述: 每个输入包含 1 个测试用例。每个测试数据的第一行包含一个整数 n (1 <= n <= 50),表示学生的个数,...

Y-puyu 的博客 691
上一篇: 判断链表是否带环,若带环求交点、环的入口点【Java实现】
下一篇: 马戏团_牛客网
ty6693
博客等级 码龄9年 92粉丝 72原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值