2025 算法创意实践挑战赛华东赛区(SD) 编程题T5题解

前四道都挺简单的,T5还有一点难度。

题意简述

nnn 堆宝石,第 iii 堆有 aia_iai 颗宝石,每隔 kkk 堆选一个,求最多能拿多少宝石。

思路

假设 n=7,k=3,a={4,6,1,2,10,1,3}n=7,k=3,a=\{4,6,1,2,10,1,3\}n=7,k=3,a={4,6,1,2,10,1,3},如图:

在这里插入图片描述
可以发现从 111 开始拿和从 k+1k+1k+1 开始拿,一定是前者更优。因为两者后续路线一定会重合,则前者会多拿第一堆,k+2,k+3,…k+2,k+3,\ldotsk+2,k+3, 同理,因此只需要处理从 i≤ki\le kik 开始拿的情况就行了。

观察从 111 开始拿,拿了 1,4,71,4,71,4,7,可以转换为不拿 2,3,5,62,3,5,62,3,5,6,因此我们有了基本思路:处理前缀和,减去不拿的部分。

在这里插入图片描述
仔细观察下标,得到第一组不拿的宝石堆为 fk+1−f1f_{k+1}-f_{1}fk+1f1,第二组不拿的宝石堆为 f1+2×k+1−f1+k+1f_{1+2\times k+1}-f{1+k+1}f1+2×k+1f1+k+1

找规律即得:

对于从 ∀i\forall ii 开始拿的情况,不需要拿的宝石数量为

Σx=1i+x×k+x−1≤n(fi+x×k+x−1−fi+(x−1)×k+x−1) \Sigma_{x=1}^{i+x\times k+x-1\le n} (f_{i+x\times k+x-1}-f_{i+(x-1)\times k+x-1}) Σx=1i+x×k+x1n(fi+x×k+x1fi+(x1)×k+x1)

但是,这一大坨式子是错的。考场60pts调了半个小时没发现

看这种情况:

在这里插入图片描述
发现了吗?在最后一堆能拿的宝石如果不是 nnn 的情况下,剩余的宝石堆数不会被剪掉。

所以正确的式子就长这样:

Σx=1i+x×k+x−1≤n(fi+x×k+x−1−fi+(x−1)×k+x−1)−(fn−fi+x×k+x)(i+x×k+x<n) \Sigma_{x=1}^{i+x\times k+x-1\le n} (f_{i+x\times k+x-1}-f_{i+(x-1)\times k+x-1})-(f_n-f_{i+x\times k+x})(i+x\times k+x\lt n) Σx=1i+x×k+x1n(fi+x×k+x1fi+(x1)×k+x1)(fnfi+x×k+x)(i+x×k+x<n)

代码

考场代码:

# include <iostream>
# define LL long long
const int N = 1e6 + 5;
LL n, k, a[N];
LL f[N], g[N], ret ( -1 );
int main () {
  std::cin.tie ( 0 ) -> sync_with_stdio ( false );
  std::cin >> n >> k;
  for ( int i ( 1 ); i <= n; i ++ )
    std::cin >> a[i], f[i] = f[i - 1] + a[i];
  for ( int i ( 1 ); i <= k + 1; i ++ ) {
    int tmp ( 0 );
    if ( i >= n - k ) {
      g[i] = a[i]; continue;
    }
    g[i] = f[n] - f[i - 1];
    for ( int x ( 1 ); ( i + x *
          k + x - 1 ) <= n; x ++ ) {
      g[i] -= f[i + x * k + x - 1] -
              f[i + ( x - 1 ) * k + x - 1];
      tmp = x;
    //   std::cout << f[i + x * k + x - 1] -
    //           f[i + ( x - 1 ) * k + x - 1] << ' ';
    }
    // std::cout << i + tmp * k + tmp - 1 << ' ';
    if ( i + tmp * k + tmp < n )
      g[i] -= f[n] - f[i + tmp * k + tmp];
    //   std::cout << f[n] - f[i + tmp * k + tmp - 1] << ' ';
    // std::cout << g[i] << '\n';
  }
  for ( int i ( 1 ); i <= n; i ++ )
    ret = std::max ( ret, g[i] );
  std::cout << ret << '\n';
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值