牛客網-網易2017筆試 合唱團(dp)
題目來源:合唱團
題目描述
有 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
思路
給出 n 個數字,要求按順序選擇 k 個,使得選出的數的乘積最大。且要求選擇的相鄰兩個人的距離必須小於等於 d.
定義 dp[n][k]
為以位置為 n
的人為結尾(位置為 n 的人就是選擇的第k個人),選擇 k 個人(包括自己)所能獲得的最大乘積
那麼問題的答案就是掃一遍 dp[k][k]
…dp[n][k]
所得的最大值就是答案。
但是這裡麵包含負數,所以我們要把 dp 拆成兩個來定義:
-
dp_max[i][j]
:以第 i 個人為結尾,選擇 j 個人所能獲得的最大乘積 -
dp_min[i][j]
:以第 i 個人為結尾,選擇 j 個人所能獲得的最小乘積
現在的問題是,我們知道當前選擇的第 k 個人是位置為 n 的人,那麼選擇的第 k-1 個人的位置在哪裡?
首先有,第 k-1 個人的位置一定>=k-1
(如果從1開始每一個都選,那麼它只能到k-1)
我們看一下題目上的約束條件,要求選擇的兩個人相鄰的距離必須小於等於 d ,那麼他的位置一定 >=n-d
,因為必須小於等於 d.
且 他的位置一定是小於等於 n-1 的.
所以假設我們要算的第 k-1 個人的位置為 l ,那麼滿足 max(k-1,n-d)<=l<=n-1
因為數裡面有負數,所以我們dp的時候,
程式碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf = 1e18;
const ll N = 50 + 10;
ll a[N], dp_max[N][N], dp_min[N][N];
int main()
{
//freopen("in.txt", "r", stdin);
ll n, k, d;
scanf("%lld", &n);
for (ll i = 1; i <= n; i++)
{
scanf("%lld", &a[i]);
dp_max[i][1] = dp_min[i][1] = a[i];
}
scanf("%lld%lld", &k, &d);
for (ll i = 1; i <= n; i++)
{
for (ll j = 2; j <= k; j++)
{
if (i >= j)
for (ll l = max(j - 1, i - d); l <= i - 1; l++)
{
dp_max[i][j] = max(dp_max[i][j], max(dp_max[l][j - 1] * a[i], dp_min[l][j - 1] * a[i]));
dp_min[i][j] = min(dp_min[i][j], min(dp_max[l][j - 1] * a[i], dp_min[l][j - 1] * a[i]));
}
}
}
ll ans = -inf;
for (ll i = k; i <= n; i++)
ans = max(ans, dp_max[i][k]);
printf("%lld\n", ans);
return 0;
}