1. 程式人生 > >牛客網-網易2017筆試 合唱團(dp)

牛客網-網易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的時候,

d p m a x [ i ] [ j ] = m a x ( d p m a x [ i ] [ j ] , m a x ( d p m a x [ l ] [ j 1 ] a [ i ] , d p m i n [ l ] [ j 1 ] a [ i ] ) ) dp_{max}[i][j] = max(dp_{max}[i][j], max(dp_{max}[l][j - 1] * a[i], dp_{min}[l][j - 1] * a[i]))

程式碼

#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;
}