合唱团

合唱团

有 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
  在CODE上查看代码片 派生到我的代码片
  1. #include <stdio.h>  
  2. #include <ctype.h>  
  3. #include <string.h>  
  4. #include <stdlib.h>  
  5. #include <limits.h>  
  6. #include <math.h>  
  7. #include <algorithm>  
  8. using namespace std;  
  9. typedef long long ll;  
  10.   
  11. int a[55];  
  12. ll f[55][15][2];  
  13.   
  14. int main(){  
  15.     int n,kk,d;  
  16.     scanf("%d",&n);  
  17.     for(int i=1;i<=n;++i){  
  18.         scanf("%d",&a[i]);  
  19.     }  
  20.     scanf("%d%d",&kk,&d);  
  21.     ll ans=0;  
  22.     for(int i=1;i<=n;i++){  
  23.         f[i][1][0]=f[i][1][1]=a[i];  
  24.         for(int j=2;j<=kk;++j){  
  25.             for(int k=i-1;k>=max(i-d,1);--k){  
  26.                 f[i][j][0]=max(f[i][j][0],max(f[k][j-1][0]*a[i],f[k][j-1][1]*a[i]));  
  27.                 f[i][j][1]=min(f[i][j][1],min(f[k][j-1][0]*a[i],f[k][j-1][1]*a[i]));  
  28.             }  
  29.         }  
  30.         ans=max(ans,max(f[i][kk][0],f[i][kk][1]));  
  31.     }  
  32.     printf("%lld\n",ans);  
  33.     return 0;  
  34. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值