网易笔试题-合唱团

有 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



解法:

动态规划的中心思想是利用空间存储接下来计算需要用得到的数据,这样直接获取数据来减少计算时间,避免重复计算。

遇到该问题时可以进行分解,首先假设第i{1,2,3,...n}个人被选,编号是j{1,2,3,...k},那么前面的i-1人只需要再选择j-1个M[i][j]=max『M[k_][j-1]*A[k_]』,k_的范围在[j-1,i],但是又由于两个相邻的编号不能相差超过d,故k_的范围是[max(j-1,i-d),i].这一点必须要理解。

例如当k=1,即是只选一个人时,M[j][1]={7,4,7}(j=1,2,3);

当选择两个人时M[j][2],其中j={2,3},因为从j中选2个人,那么2<=j<=n;

例如 5,8,4,9

M[1][1]=5

M[2][1]=8

M[3][1]=4

M[4][1]=9


M[2][2]=A[2]*M[1][1]

M[3][2]=max(A[3]*M[2][1],A[3]*M[1][1])

M[4][2]=max(A[4]M[3][1],A[4]M[2][1],A[4]M[1][1])【若d=2,则M[1][1]距离A[4]的距离为3,超过范围】


M[3][3]=A[3]*M[2][2]

M[4][3]=max(A[4]*M[3][2],A[4]M[2][2])


M[4][4]=A[4]*M[3][3]

另外当存在负值时,就记录下最小值,这样本次的最大值就是上次的最小值(可能是复制)×当前的值 与 最大值×当前的值 的最大值。这样就可以更新最大值与最小值。

        
#include<iostream>
#include<math.h>
#include <climits>
#include<fstream>

using namespace std;

int main(){
    int n;
    cin>>n;
    int *A=(int*)malloc(sizeof(int)*(n+1));
    for(int i=1;i<=n;i++)
        cin>>A[i];
    int k;
    int d;
    cin>>k>>d;
//申二维数组[n+1][k+1],存放数据,一共有n个,而只选择k个,从1开始计数
    long **F=(long**)malloc(sizeof(long*)*(n+1));
	long **H=(long**)malloc(sizeof(long*)*(n+1));

	for(int i=0;i<n+1;i++)
	{
		F[i]=(long*)malloc(sizeof(long)*(k+1));
		H[i]=(long*)malloc(sizeof(long)*(k+1));
	}
	for(int i=1;i<=n;i++)
	{
		F[i][1]=A[i];
		H[i][1]=A[i];
	}
	//这里面有三个量容易搞混淆,i,j,k_,可以参照上面的例子进行
	for(int j=2;j<=k;j++)//选择k个
	{
		for(int i=j;i<=n;i++)
		{
			long Max=LONG_MIN;
			long Min=LONG_MAX;
			for(int k_=max(j-1,i-d);k_<i;k_++)//max(j-1,i-d)的意思是检验两次的距离不大于d
			{//例如求M[4][2]的值,其中d=2
				long res_Max=max(F[k_][j-1]*A[i],H[k_][j-1]*A[i]);//每次进加的一个是i,所以要乘上A[i]
				long res_Min=min(F[k_][j-1]*A[i],H[k_][j-1]*A[i]);
				if(res_Max>Max) Max=res_Max;
				if(res_Min<Min) Min=res_Min;
			}
			F[i][j]=Max;//找到当前的最大值进行填入
			H[i][j]=Min;//最小值进行填入
		}
	}
	long res=LONG_MIN;
	for(int i=k;i<=n;i++)//遍历所有的k,找到最大值
		if(res<F[i][k])
			res=F[i][k];
	cout<<res<<endl;

	for(int i=0;i<n+1;i++)
	{
		free(F[i]);
		free(H[i]);
	}
    free(F);
    free(H);
	free(A);
    return 0;
}


如果不明白欢迎讨论




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值