继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

动态规划——看似dp的贪心问题最大乘积(蓝桥杯试题集)

九日王朝
关注TA
已关注
手记 180
粉丝 41
获赞 185

题目链接:


http://lx.lanqiao.cn/problem.page?gpid=T136


问题描述
  对于n个数,从中取出m个数,如何取使得这m个数的乘积最大呢?
输入格式
  第一行一个数表示数据组数
  每组输入数据共2行:
  第1行给出总共的数字的个数n和要取的数的个数m,1<=n<=m<=15,
  第2行依次给出这n个数,其中每个数字的范围满足:a[i]的绝对值小于等于4。
输出格式
  每组数据输出1行,为最大的乘积。
样例输入
1
5 5
1 2 3 4 2
样例输出
48 


一:首先要分离正负,两个负值乘积为正。做这道题时让我想起了按键盘http://blog.csdn.net/sm9sun/article/details/53241628

 与其一样的是,按键盘保留大小写两种状态,那么本题也要保留正负最大值两种状态

二:类似于01背包,我们把m当作容量,每个数的体积都是1

这样一来每个数都面临取或者不取两种状态,当我们遍历到第i个物品时,我们要保留在i个物品中取j个物品的最大正负值即可



#include<stdio.h>#include<math.h>double fmax(double a,double b){	return a>b?a:b;}double fmin(double a,double b){	return a<b?a:b;}int main(){	double a[20];double fdp[20][20][20];double dp[20][20][20];	int m,n,i,j,k,t;	scanf("%d",&t);	while(t--)	{		scanf("%d%d",&n,&m);for(i=1;i<=n;i++)		scanf("%lf",&a[i]);for(i=0;i<=n;i++)		for(j=0;j<=m;j++)	for(k=0;k<=j;k++)	{	dp[i][j][k]=1;		fdp[i][j][k]=1;	}	fdp[0][1][1]=dp[0][1][1]=a[1];	for(i=1;i<=n;i++)                   //遍历n个数{for(j=1;j<=m&&j<=i;j++)             //m为容量,从i个数中取j个数{        	for(k=1;k<=j;k++)           //k遍历当我容量上限为j时取不同个数的最大值,这里与01背包不同,取值必须达到m容量,不能小于        	{                           //所以每一个k都要记录下来if(a[i]>0){dp[i][j][k]=fmax(dp[i-1][j][k],dp[i-1][j-1][k-1]*a[i]);fdp[i][j][k]=fmin(fdp[i-1][j][k],fdp[i-1][j-1][k-1]*a[i]);}else{dp[i][j][k]=fmax(dp[i-1][j][k],fdp[i-1][j-1][k-1]*a[i]);fdp[i][j][k]=fmin(fdp[i-1][j][k],dp[i-1][j-1][k-1]*a[i]);}			// printf("%.0lf,%.0lf ",dp[i][j][k],fdp[i][j][k]);}//printf("---");}//printf("\n");}printf("%.0lf\n",dp[n][m][m]);}return 0;}


用dp思想的确可以解决这道题,不过后来看了网上的答案,发现其实还有更简单的方法,用贪心~


我们将正负数分组排序后,依次选取两个最大正值乘积,两个最大负值乘积即可~

注意,如果m为奇数时要再乘一个正数。



#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int main(){int t, n, m;int num[20];scanf("%d", &t);while(t--){scanf("%d %d", &n, &m);for(int i = 0; i < n; i++)scanf("%d", &num[i]);sort(num, num+n);int i, j, a, b;i = j = 0;long long ans = 1;while(m){a = num[i]*num[i+1];b = num[n-j-1]*num[n-j-2];if(a>=b && m>=2){ans*=a;i+=2;m-=2;}else{ans*=num[n-j-1];j++;m--;}}printf("%lld\n", ans);}return 0;}

所以说,也不能盲目的运用dp,有一些问题还是可以用贪心解决的。


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP