1. 程式人生 > >網易內推題,合唱團,C++實現

網易內推題,合唱團,C++實現

有 n 個學生站成一排,每個學生有一個能力值.牛牛想從這 n 個學生中按照順序選取 k 名學生. 要求相鄰兩個學生的位置編號的差不超過 d,使得這 k 個學生的能力值的乘積最大,你能返回最大的乘積嗎?

輸入描述:

每個輸入包含 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
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
void Hechang()
{
	int n, K, d;
	cin >> n;
	vector<int> nums(n);
	for (int i = 0; i < n; i++)
		cin >> nums[i];
	cin >> K >> d;
	//dp_max[k][i]表示當選中了k個學生,並且以第i個學生為結尾,所產生的最大乘積.
	vector<vector<long long>> dp_max(K + 1, vector<long long>(n + 1, 0));
	vector<vector<long long>> dp_min(K + 1, vector<long long>(n + 1, 0));
	long long res = LLONG_MIN;
	for (int i = 0; i < n;i++)
	{
		//狀態開頭的初始情況,如果只選了一個學生,那麼這個人就是第i個學生。
		dp_max[1][i] = dp_min[1][i] = nums[i];
		//從k=2開始迴圈,進入狀態解釋方程。先解決子問題,也就是人少的時候的dp_max,dp_min,然後遞推。
		for (int k = 2; k <= K;k++)
		{
			for (int j = i - 1; j > -1 && i - j <= d;j--)
			/*j初始是i左邊一位,也即末尾左邊一位的數。
			但它或許與再之前乘積最大數的乘積不是最大,因此需要j--找再之前最大的數。
			全部迴圈完畢以後,找到乘積最大的末尾位置,作為dp_max[k-1][j]提供給dp_max[k][i]。
			即每個i都有一個dp_max[K][i],但當我們計算dp_max[K+1][i+1]時,需要找到dp_max[K][i]最大的那個i。
			相鄰的人之間差距不能超過D,然後反向推導。含0的陣列都空出來了所以j>0。*/			
			{
				dp_max[k][i] = max(dp_max[k][i], max(dp_max[k - 1][j] * nums[i], dp_min[k - 1][j] * nums[i]));
				dp_min[k][i] = min(dp_min[k][i], min(dp_min[k - 1][j] * nums[i], dp_max[k - 1][j] * nums[i]));
			/*因為上一個為最大負值也可能乘以一個負數變為正數。
			同時由於k個人變成了k+1個人,因此原來結尾是i,現在結尾變成了i+1。
			先由k=2開始實現迴圈,找出dp_max[2][i],dp_min[2][i],由此再通過遞推式找出dp_max[3][i],dp_min[3][i]直至dp_max[K][i]。
			注意i也是在迴圈,因為結尾不可能永遠是一個值,因此i也要不斷變化。*/
			}
		}
		res = max(res, dp_max[K][i]);
	}
	cout << res;
}
int main()
{
	Hechang();
	system("pause");
	return 0;
}