合唱团

    xiaoxiao2025-05-19  6

    合唱团

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

    网址:http://www.nowcoder.com/questionTerminal/661c49118ca241909add3a11c96408c8

    ……想了半天,居然想不出来如何解释状态转移方程是怎么想出来的。这只能说,是套路。

    这个思考过程待填,下面直接说状态的定义。

    f [ i ] [ j ] [ 最大 / 最小 ]

    分别表示,以第i个人为最后一个(也是必选的)人,加上这个人,已经选了 j 个人,最大可能的乘积和最小可能的乘积。

    ——为什么不是只记录最大的,还要记录最小的?

    ——因为最小的,很可能是一个负数,有着极大的绝对值,再乘一个负数,就变成最大的正数,也就是最优解了。

    然后考虑,这个状态由哪些状态转移过来?

    j 人,明显是从j-1个人的状态,最后加1个人(当前考虑的 i )而来。

    第 i 人,根据题目要求,编号差不能大于d。那我们就往前观察最多d个人,从i-d到i-1,选了j-1个人中,选择和自己相乘,最大/最小的。

    注意考虑边界条件:只选了一个人,就是 i 自己。

    最后,解很大,请使用long long(C++)/ long (Java、C#)来保存中间计算结果。

    [cpp]  view plain  copy   #include <stdio.h>   #include <ctype.h>   #include <string.h>   #include <stdlib.h>   #include <limits.h>   #include <math.h>   #include <algorithm>   using namespace std;   typedef long long ll;      int a[55];   ll f[55][15][2];      int main(){       int n,kk,d;       scanf("%d",&n);       for(int i=1;i<=n;++i){           scanf("%d",&a[i]);       }       scanf("%d%d",&kk,&d);       ll ans=0;       for(int i=1;i<=n;i++){           f[i][1][0]=f[i][1][1]=a[i];           for(int j=2;j<=kk;++j){               for(int k=i-1;k>=max(i-d,1);--k){                   f[i][j][0]=max(f[i][j][0],max(f[k][j-1][0]*a[i],f[k][j-1][1]*a[i]));                   f[i][j][1]=min(f[i][j][1],min(f[k][j-1][0]*a[i],f[k][j-1][1]*a[i]));               }           }           ans=max(ans,max(f[i][kk][0],f[i][kk][1]));       }       printf("%lld\n",ans);       return 0;   }  
    转载请注明原文地址: https://ju.6miu.com/read-1299037.html
    最新回复(0)