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

记录一下:从此题开始正式慢慢研究一下动态规划。源代码来自牛客网;

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

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

输入:
3 7 4 7 2 50
输出:
49

---------------------------------------------------------------------------------------------------------------------------------------------------------------

         首先谈下动态规划吧,这个是算法里面一直比较难的,当我拿到这个题的时候,有点难以下手,虽然知道要用动态规划但是如何用,自己完全不知道,首先想到找出这个n个数中k个最大的相乘  ,但是很遗憾不对,①要求相邻两个学生之间的编号差不能超过d,②能力值存在负数。
         动态规划是要将问题分解若干子问题,同时子问题之间可能存在包含,我们通常需要保存子问题的结果。
         怎么保存了?数组f[n][m]表示选了n个人方案,最后一的位置为m,那f[n-1][p]就表示选了n-1个人,最后一个位置为p
         怎么分解呢?首先m个人中选n个编号差要求<=d,若已经知道选n-1人的方案,同时这个方案最后一个人的位置为p,我们只需要遍历p+1到p+d的位置求得第n个人的位置。选n个人实际上不知道哪一个作为结尾所以会遍历的求f[n][1]~f[n][m]的最大值
        有负数怎么办?因为有负数所以我们在加一个数组fmin[n][m]表示m个人中选n个间隔为d最小的乘积,最小的也可能成为最大的。
                                          f[n][m]=max(f[n][m],max(f[n-1][m-c])*arr[m],fmin[n-1][m-c]*arr[m])
arr[]表示能力值数组,c为间隔,这个表达式要从1循环到d进行计算,当n==1,f[n][m]=fmin[n][m]=arr[m];这个时候肯定会有疑问,为什么m个人当中选1个人就取arr[m],这不符合要求!其实没有关系,m是从1开始取的,也就是说
                                                         f[1][1]=arr[1],f[1][2]......f[1][50]=arr[50]
最后通过循环会一直取max,所以也不存在上面问题.
假设 arr[]={10,21,30,41,45,-2,-7,13},设d为5,我们在计算f[2][6]的时候,k=5


for(int c=1;c<=d;c++)
{
      f[2][6]=max(f[2][6],max(f[1][6-c])*arr[6],fmin[1][6-c]*arr[6]);
      fmin[2][6]=min(fmin[2][6],min(fmax[1][6-c]*arr[6],fmin[1][6-c]*arr[6]));
}

这样就能求出f[2][6],fmin[2][6],一直到最后求出f[k][n]; 最后还需要注意用int会溢出需要用  long long int 
根据表格就是:要求f[2][6],就是求 f[1][5]、f[1][4]、f[1][3]、f[1][2]、f[1][1]、f[1][0]这5个数和arr[6] 乘积最大值,再和f[2][6]比较取最大

f[n][m]m12345678
n         
1 1021304145-213-7
2 021063012301845058514
3 0063002583055350023985630
4 0002583001162350071955025830
5 000011623500015110550774900
6         
7         
8         

从结果可以看出最终的答案为f[5][7],也就是说选5个人的方案结尾为7的情况最优,建议不懂的时候认真看看这个表格


#include<iostream>
using namespace std;
/*记第k个人的位置为one,则可以用f[one][k]表示从n个人中选择k个的方案。然后,它的子问题,
需要从one前面的left个人里面,选择k-1个,这里left表示k-1个人中最后一个(即第k-1个)人的位置,因此,子问题可以表示成f[left][k-1].*/
int main()
{
	int n,k,d;
    long long  int res;
	
	res=0;
	cin>>n;
	int *arr=new int[n];
	for(int i=0;i<n;i++)
	{
		cin>>arr[i];
	}

	cin>>k;            //选取的 k名学生
	cin>>d;            //相隔编号不超过  d
	long long int fmax[11][50]={0};   
	long long int fmin[11][50]={0};

	for(int j=1;j<=k;j++)
	{
		for(int i=0;i<n;i++)
		{
			if(j==1)
			{
				fmax[j][i]=arr[i];
				fmin[j][i]=arr[i];
				//continue;
			}
			for(int p=1;p<=d;p++)
			{
				if(i-p>=0 && i-p<n)
				{
					fmax[j][i]=max(fmax[j][i],max(fmax[j-1][i-p]*arr[i],fmin[j-1][i-p]*arr[i]));                                //  在i个数中选取j个数   最大的成绩
					fmin[j][i]=min(fmin[j][i],min(fmax[j-1][i-p]*arr[i],fmin[j-1][i-p]*arr[i]));
				}

			}
            res=max(res,fmax[k][i]);
		}
		
	}

	cout<<res;

	if(arr !=NULL)
	{
		delete []arr;
		arr=NULL;
	}


	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值