CF1442D-Sum

发布时间 2023-08-25 19:51:38作者: Ciaxin

Sum

You are given \(n\) non-decreasing arrays of non-negative numbers.

Vasya repeats the following operation \(k\) times:

  • Selects a non-empty array.
  • Puts the first element of the selected array in his pocket.
  • Removes the first element from the selected array.

Vasya wants to maximize the sum of the elements in his pocket.

solution

1、通过微调。

首先在最终状态下,假设数组 \(a,b\) 中的元素分别被取到了 \(i,j\)(并没有取完),且 \(a_{i} \leq b_{j}\)

由于数组内元素不降的性质,可以得知 \(a_{i-1} \leq a_{i}\),那么 \(a_{i-1}\leq b_{j}\)

可以之前发现取 \(a_{i-1}\) 是不一定比 \(b_{j}\) 优的。

因此可以得到一条结论:在最终状态下,最多只会存在一个数组 \(x\) 被取了部分。

2、冻柜

对于其他的数组,要么不选,要么全选。

那么就可以将其转化为 \(n\) 个具有大小、价值的物品,进行 \(01\) 背包。

枚举那个被取了部分的数组 \(x\),枚举数组 \(x\) 被取出的元素的个数,时间复杂度为 \(n^2k\) 的。

3、分治优化

\(m = \left\lfloor\frac{l+r}{2}\right\rfloor\)

在位于 \([l,m]\) 中的数组 \(x\),都需要来自 \((m,r]\) 的数组的 dp 值。

那么对于 \([l,m]\) 中的数组 \(x\),可以将 \((m,r]\) 的 dp 值预处理出来。

该过程可以递归操作,直到 \(l=r\) 的时候,对于第 \(l\) 个数组,需要的 dp 值是都已经求出来的。

时间复杂度降到 \(kn\log_2n\)

code

点击查看代码
#include <bits/stdc++.h>
using namespace std;

vector<long long> a[3010];
long long dp[3010], sum[3010], len[3010], n, k;

long long DFS(int l, int r)
{
    if (l == r) {
        long long res = 0, ans = -1;
        for (int i = 0; i <= min(k, len[l]); i ++ ) {
            ans = max(ans, dp[k - i] + res);
            if (i < len[l]) res += a[l][i];
        }
        return ans;
    }
    long long f[3010], mid = (l + r) >> 1, ans = -1;
    memcpy(f, dp, sizeof(dp));

    // 对于[l,mid]的数组,预处理(mid,r]的dp值
    for (int i = mid + 1; i <= r; i ++ ) 
        for (int j = k; j >= len[i]; j -- ) 
            dp[j] = max(dp[j], dp[j - len[i]] + sum[i]);
    ans = max(ans, DFS(l, mid)); // 递归
    memcpy(dp, f, sizeof(f));
    
    for (int i = l; i <= mid; i ++ )
        for (int j = k; j > len[i]; j -- )
            dp[j] = max(dp[j], dp[j - len[i]] + sum[i]);
    ans = max(ans, DFS(mid + 1, r));
    return ans;
}

int main()
{
    scanf("%lld%lld", &n, &k);
    for (int i = 1, x; i <= n; i ++ ) {
        scanf("%lld", &len[i]);
        for (int j = 1; j <= len[i]; j ++ ) {
            scanf("%lld", &x);
            a[i].push_back(x); sum[i] += x;
        }
    }
    printf("%lld", DFS(1, n));
    return 0;
}