合唱团
题目
- 有 n 个学生站成一排,每个学生有一个能力值,牛牛想从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 k 个学生的能力值的乘积最大,你能返回最大的乘积吗?
输入描述:
- 每个输入包含 1 个测试用例。每个测试数据的第一行包含一个整数 n ( 1≤n≤50 1 ≤ n ≤ 50 ),表示学生的个数。
接下来的一行,包含 n 个整数,按顺序表示每个学生的能力值 a0,a1,…,an−1 a 0 , a 1 , … , a n − 1 ( −50≤ai≤50 − 50 ≤ a i ≤ 50 )。
接下来的一行包含两个整数,k ( 1≤k≤10 1 ≤ k ≤ 10 )和 d ( 1≤d≤50 1 ≤ d ≤ 50 ).
输入例子:
3
7 4 7
2 50
输出例子:
49
解题思路
题目难点
- 学生能力值有正负之分
- 如何利用学生编号差不超过d这个条件
- 最大乘积的取值大小, 如果采用int类型会不会溢出
设dpMax[i][j]是以ai为最后一个学生,长度为j+1的序列的最大乘积.设dpMin[i][j]是以ai为最后一个学生,长度为j+1的序列的最小乘积.
设
d
p
M
a
x
[
i
]
[
j
]
是
以
a
i
为
最
后
一
个
学
生
,
长
度
为
j
+
1
的
序
列
的
最
大
乘
积
.
设
d
p
M
i
n
[
i
]
[
j
]
是
以
a
i
为
最
后
一
个
学
生
,
长
度
为
j
+
1
的
序
列
的
最
小
乘
积
.
当 j=0 j = 0 时容易有
dpMax[i][0]=dpMin[i][0]=ai
d
p
M
a
x
[
i
]
[
0
]
=
d
p
M
i
n
[
i
]
[
0
]
=
a
i
对 dpMax[i][j] d p M a x [ i ] [ j ] , dpMin[i][j] d p M i n [ i ] [ j ] 很容易有如下定义
dpMax[i][j]=max(ai∗tempMax,ai∗tempMin)dpMin[i][j]=min(ai∗tempMax,ai∗tempMin)
d
p
M
a
x
[
i
]
[
j
]
=
m
a
x
(
a
i
∗
t
e
m
p
M
a
x
,
a
i
∗
t
e
m
p
M
i
n
)
d
p
M
i
n
[
i
]
[
j
]
=
m
i
n
(
a
i
∗
t
e
m
p
M
a
x
,
a
i
∗
t
e
m
p
M
i
n
)
对于 tempMax t e m p M a x , tempMin t e m p M i n 有如下定义(这里之所以还要求最小值的原因是因为存在负数的情况)
{tempMax=max(dpMax[i−1][j−1],dpMax[i−2][j−1],…,dpMax[i−d][j−1])tempMin=min(dpMin[i−1][j−1],dpMin[i−2][j−1],…,dpMin[i−d][j−1])
{
t
e
m
p
M
a
x
=
m
a
x
(
d
p
M
a
x
[
i
−
1
]
[
j
−
1
]
,
d
p
M
a
x
[
i
−
2
]
[
j
−
1
]
,
…
,
d
p
M
a
x
[
i
−
d
]
[
j
−
1
]
)
t
e
m
p
M
i
n
=
m
i
n
(
d
p
M
i
n
[
i
−
1
]
[
j
−
1
]
,
d
p
M
i
n
[
i
−
2
]
[
j
−
1
]
,
…
,
d
p
M
i
n
[
i
−
d
]
[
j
−
1
]
)
问题分解示意图
代码
#include <iostream>
#include <vector>
#define max(a,b) ((a) > (b) ? (a) : (b))
#define min(a,b) ((a) < (b) ? (a) : (b))
using namespace std;
// 学生能力数组
vector<long long> a;
int main()
{
// 输入实例
int n, k ,d;
cin >>n;
for(int idx=0; idx<n; ++idx){
long long power;
cin >> power;
a.push_back(power);
}
cin >> k >> d;
// 初始化成dpMax[n][k]二维数组
vector< vector<long long> >dpMax(n);
for(int idx=0; idx<n; ++idx){
dpMax[idx].resize(k);
}
// 初始化成dpMin[n][k]二维数组
vector< vector<long long> >dpMin(n);
for(int idx=0; idx<n; ++idx){
dpMin[idx].resize(k);
}
// 迭代开始之前的初始值dpMax[i][0] = dpMin[i][0] = a[i]
for(int i=0; i<n; ++i){
dpMax[i][0] = a[i];
dpMin[i][0] = a[i];
}
for(int j=1; j<=k-1; ++j){
// 这里从i=j开始迭代是因为序列长度为j+1,则最后一个学生的下标至少为j
for(int i=j; i<=n-1; ++i){
long long tempMin, tempMax;
tempMax = dpMax[i-1][j-1];
tempMin = dpMin[i-1][j-1];
// 求出
// max(dpMax[i-1][j-1], dpMax[i-2][j-1], ..., dpMax[i-d][j-1])
// min(dpMin[i-1][j-1], dpMin[i-2][j-1], ..., dpMin[i-d][j-1])
//
// 值得注意的是这里的除了满足 p>=i-d 以外 还应该满足 p>=j-1 的条件
// 因为子序列长度为j, 则最后一个学生的下标至少为j-1, 以保证长度为j
for(int p=i-1; p >= i-d && p>=j-1; --p){
tempMax = max(dpMax[p][j-1], tempMax);
tempMin = min(dpMin[p][j-1], tempMin);
}
dpMax[i][j] = max(tempMax*a[i], tempMin*a[i]);
dpMin[i][j] = min(tempMax*a[i], tempMin*a[i]);
}
}
// 求出max(dpMax[k-1][k-1], dpMax[k][k-1],..., dpMax[n-1][k-1])中最大的数
long long result = dpMax[k-1][k-1];
for(int i = k-1; i< n; ++i){
result = max(dpMax[i][k-1], result);
}
cout << result << endl;
return 0;
}