校招编程——合唱团
-
题目链接:合唱团
-
题目描述
- 有 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
-
时间限制:1000 ms,空间限制:32768 K
-
思路分析
- 动态规划,假设已经选好了 index 个学生,那么下一个学生选还是不选取决于与他相乘产生的价值是否大于当前已存在的最大价值,对于第 i 个学生,他前面的第 j 个学生与他价值之积就是产生状态变化的原因,得到状态转移方程:\(value[index][i] = max(value[inex][i],value[index-1][j]*data[i])\)
- 由于题目中输入数据存在负值,因此需要再开一个数组计算负值,并且在状态方程中加入负数之积(偶数个相乘为正数)的判断
-
代码
#include <iostream> #include <cstdio> using namespace std; typedef long long int ll; int n,k,d; ll ans; ll data[55],a[11][55],b[11][55]; int main() { int i,j; scanf("%d",&n); for(i = 1; i <= n; i++) scanf("%lld",&data[i]); scanf("%d%d",&k,&d); for(i = 0; i <= k; i++) for(j = 0; j <= n; j++,a[i][j] = b[i][j] = 0); for(i = 1,ans = 0; i <= n; i++) { a[1][i] = b[1][i] = data[i]; for(int index = 2; index <= k; index++) { for(j = i-1; j > 0 && i-j <= d; j--) { a[index][i] = max(a[index][i],max(a[index-1][j]*data[i],b[index-1][j]*data[i])); b[index][i] = min(b[index][i],min(a[index-1][j]*data[i],b[index-1][j]*data[i])); } } ans = max(ans,a[k][i]); } printf("%lld\n",ans); return 0; }