Codeforces 1175 D Array Splitting 题解(DP+贪心)

题目:CF1175D.
题目大意:给定一个长度为 n n n的数组 a a a,要求划分成 k k k块(不能有块为空).设 a i a_i ai所在块的编号为 f ( i ) f(i) f(i),则求出最大的 ∑ i = 1 n a i f ( i ) \sum_{i=1}^{n}a_if(i) i=1naif(i).
1 ≤ k ≤ n ≤ 3 ∗ 1 0 5 , ∣ a i ∣ ≤ 1 0 6 1\leq k\leq n\leq 3*10^5,|a_i|\leq 10^6 1kn3105,ai106.

看到这道题的时候感觉像是个DP,然后一看数据范围 1 ≤ k ≤ n ≤ 3 ∗ 1 0 5 1\leq k\leq n\leq 3*10^5 1kn3105,然而我能想到的状态都是 O ( n k ) O(nk) O(nk)的…

于是后面开始死命想贪心,想到大概只剩半个小时的时候,实在感觉贪心不行,只好硬着头皮DP了…

先是想了个顺着推的DP,设 f [ i ] [ j ] f[i][j] f[i][j]表示 a 1 a_1 a1 a i a_i ai划分成 j j j组的最大答案,容易想到DP方程:
f [ i ] [ j ] = max ⁡ { f [ i − 1 ] [ j ] , f [ i − 1 ] [ j − 1 ] } + a [ i ] ∗ j f[i][j]=\max \{ f[i-1][j],f[i-1][j-1]\}+a[i]*j f[i][j]=max{f[i1][j],f[i1][j1]}+a[i]j

发现这个方程与 i i i j j j都有关,一下子就觉得没办法优化了…

所以再想了个倒着推的DP,设 f [ i ] [ j ] f[i][j] f[i][j]表示 a i a_i ai a n a_n an划分成 j j j组的最大答案,容易列出方程:
f [ i ] [ j ] = max ⁡ { f [ i + 1 ] [ j ] + a [ i ] , f [ i + 1 ] [ j − 1 ] + ∑ k = i n a [ k ] } = max ⁡ { f [ i + 1 ] [ j ] , f [ i + 1 ] [ j − 1 ] + ∑ k = i + 1 n a [ k ] } + a [ i ] f[i][j]=\max \left\{ f[i+1][j]+a[i],f[i+1][j-1]+\sum_{k=i}^{n}a[k] \right\}\\ =\max \left\{ f[i+1][j],f[i+1][j-1]+\sum_{k=i+1}^{n}a[k] \right\}+a[i] f[i][j]=max{f[i+1][j]+a[i],f[i+1][j1]+k=ina[k]}=max{f[i+1][j],f[i+1][j1]+k=i+1na[k]}+a[i]

然后我们设 A A A数组为 a a a的后缀和,即 A [ i ] = ∑ j = i n a [ j ] A[i]=\sum_{j=i}^{n}a[j] A[i]=j=ina[j],就可以把方程变为:
f [ i ] [ j ] = max ⁡ { f [ i + 1 ] [ j ] , f [ i + 1 ] [ j − 1 ] + A [ i + 1 ] } + a [ i ] f[i][j]=\max\{ f[i+1][j],f[i+1][j-1]+A[i+1] \}+a[i] f[i][j]=max{f[i+1][j],f[i+1][j1]+A[i+1]}+a[i]

然后我们会发现,这个东西相当于是在 A [ 1 ] A[1] A[1] A [ n ] A[n] A[n]中选 k k k个数并且强制选 A [ 1 ] A[1] A[1]的情况下,使得和最大.

很容易想到这个东西可以转化为贪心,给 A [ 2 ] A[2] A[2] A [ n ] A[n] A[n]排下序找到前 k − 1 k-1 k1大求个和再加个 A [ 1 ] A[1] A[1]就是答案.

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn).

代码如下:

#include<bits/stdc++.h>
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=300000;

int n,k;
LL a[N+9],A[N+9],ans;

bool cmp(const LL &a,const LL &b){return a>b;} 

Abigail into(){
  scanf("%d%d",&n,&k);
  for (int i=1;i<=n;++i)
    scanf("%I64d",&a[i]);
}

Abigail work(){
  for (int i=n;i>=1;--i) A[i]=a[i]+A[i+1];
  sort(A+2,A+n+1,cmp);
  for (int i=1;i<=k;++i) ans+=A[i];
}

Abigail outo(){
  printf("%I64d\n",ans);
}

int main(){
  into();
  work();
  outo();
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值