1 条题解

  • 0
    @ 2025-8-24 22:48:38

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar ylch
    博观而约取,厚积而薄发 加油!(查看.com域名帖子中的内容,把后缀改成.me即可)

    搬运于2025-08-24 22:48:38,当前版本为作者最后更新于2025-07-22 23:35:31,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    有一个很妙的思路,分享一下。

    根据题目的意图,我们要在移动距离最短前提下,使得数组的前 FF 个元素的和大于等于 TT。显然只能从后往前移动,不然无意义。

    设选中的 FF 个元素在原数组中的位置为 x1x2xFx_1 \leq x_2 \leq \dots \leq x_F,经过交换后在前 FF 个位置的最终位置为 y1<y2<<yFy_1 < y_2 < \dots < y_F(其中 yi[1,F]y_i \in [1,F])。

    将元素从 xix_i 移动到 yiy_i 需要 xiyi|x_i - y_i| 次相邻交换,总交换次数即为 i=1F(xiyi)\sum\limits_{i=1}^F (x_i - y_i),因为 xiyix_i \geq y_i

    显然不管怎么交换,原来位置和 xi\sum x_i 一定等于交换后的位置和 yi\sum y_i 加上交换时移动的距离(就是交换次数)

    设交换次数为 tt,则 xi=yi+t \sum x_i = \sum y_i + t,移项可得 t=xiyit = \sum x_i - \sum y_i

    可得对于前 FF 个位置,yiy_i 固定取 1,2,3,F1,2,3,\dots F,位置和等于 F(F+1)2\frac{F(F+1)}{2}

    所以,最小交换次数的计算方法如下:

    t=xiF(F+1)2t=\sum x_i - \frac{F(F+1)}{2}

    要使得交换次数 tt 最小,就要使得 xi\sum x_i 尽可能小。


    所以,我们要选中 FF 个数值总和大于等于 TT 的数,在数值总和都大于等于 TT 时就让位置和最小。

    不难想到这一步可以用 dp 解决。设 dpi,j,kdp_{i,j,k} 表示当前在位置 ii,选了 jj 个元素,选中的所有元素下标和为 kk 时的最大数值总和。

    转移方程比较简单,只要对于每个不同的元素 ii,尝试加入之前不同的状态就行了:

    $$dp_{i,j,k} = \max\{ dp_{i-1,j-1,k-i} + a_i \}\ (j \le \min\{i,F\}) $$

    转移时可以滚动数组,不过要倒序枚举 jj

    时间复杂度约为 O(5000×nF)O(5000 \times nF)100(100+1)2=5050\frac{100(100+1)}{2}=5050)。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    ll N, F, T;
    ll a[105], dp[105][5100]; // dp[j][k]:(当前第i个数)选了j个,下标和为k时,数值和的最大值
    
    void solve()
    {
        cin >> N >> F >> T;
        for(int i = 1; i <= N; i ++){
            cin >> a[i];
        }
    
        memset(dp, -0x3f, sizeof dp);
        dp[0][0] = 0;
        for(int i = 1; i <= N; i ++){
            for(int j = min(1LL*i, F); j >= 1; j --){
                for(int k = i; k <= 5050; k ++){ // 100*(100+1)/2 = 5050
                    dp[j][k] = max(dp[j][k], dp[j - 1][k - i] + a[i]);
                }
            }
        }
    
        int minp = 1e9;
        for(int k = 0; k <= 5050; k ++){
            if(dp[F][k] >= T) minp = min(minp, k);
        }
        if(minp == 1e9) puts("NO"); else cout << max(0LL, 1LL*minp - F*(F+1)/2) << '\n';
    }
    
    signed main()
    {
        ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
        solve();
        return 0;
    }
    
    
    • 1

    信息

    ID
    8937
    时间
    3000ms
    内存
    1024MiB
    难度
    4
    标签
    递交数
    0
    已通过
    0
    上传者