输入描述:
每个输入包含 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; }如果不明白欢迎讨论