合唱团 题目描述 有 n 个学生站成一排,每个学生有一个能力值,牛牛想从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 k 个学生的能力值的乘积最大,你能返回最大的乘积吗?
输入描述 每个输入包含 1 个测试用例。每个测试数据的第一行包含一个整数 n (1 <= n <= 50),表示学生的个数,接下来的一行,包含 n 个整数,按顺序表示每个学生的能力值 ai(-50 <= ai <= 50)。接下来的一行包含两个整数,k 和 d (1 <= k <= 10, 1 <= d <= 50)。
输出描述 输出一行表示最大的乘积。
解法分析 这道题最笨的方法也是最容易想到的方法就是遍历. 也就是说,把所有可能的tuple都试一遍. 把tuple中所有数乘积最大的数输出出来就好了.
在不考虑时间和空间复杂度的情况下,所谓一力降十会. 用遍历方法可以解决所有的决策问题. 然而不管是在笔试题当中还是在实际的应用中(比如在编码领域).那么遍历方法可以说是噩梦级的存在了.该题若采用遍历的算法,在给定的空间复杂度的要求之下,AC rate为80%.
这个题的正解是采用动态规划的算法.表面上看是一个很高端的名词,其实就好比走路. 下一步的捷径可以通过前一步或者前几步的捷径递推得到.
通过这个题,也看出了遍历在动态规划的面前有多么的苍白无力.只用了相对于遍历而言很少的内存和时间就可以得到最优解.
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include <iostream> #include <algorithm> #include <climits> using namespace std ;int main () { int n; cin >> n; int data[n]; for (int i =0 ; i < n; i++) { cin >> data[i]; } int k, d; cin >> k >> d; long int maxvalue[n][k]; long int minvalue[n][k]; for (int i =0 ; i < n; i++) { for (int j = 0 ; j < k; j++) { maxvalue[i][j] = LONG_MIN; minvalue[i][j] = LONG_MAX; } } long int res = LONG_MIN; for (int i = 0 ; i < n; i++) { maxvalue[i][0 ] = minvalue[i][0 ] = data[i]; } for (int i = 0 ; i < n; i++) { for (int j = 1 ; j <= min(i, k-1 ); j++) { for (int l = i-1 ; l >= max(0 , max(i - d, j-1 )); l--) { maxvalue[i][j] = max(maxvalue[i][j], max(maxvalue[l][j-1 ] * data[i], minvalue[l][j-1 ] * data[i])); minvalue[i][j] = min(minvalue[i][j], min(maxvalue[l][j-1 ] * data[i], minvalue[l][j-1 ] * data[i])); } } } for (int i = k-1 ; i < n; i++) { res = max(res, maxvalue[i][k-1 ]); } cout << res; }