Codeforces 1253 C. Sweets Eating 做题记录(DP)

  原题链接:https://codeforces.com/contest/1253/problem/C

  很明显,贪心策略是先吃甜度大的可以保证最终的总甜度最小,因此我们先从小到大排个序。一天可以吃$m$个,因此我们对于每个$k$,就让甜度前$1~m$名在第一天吃,前$m+1~2m$名第二天吃……以此类推。我们可以写个相对若智的暴力解法。(理所当然会TLE哒!)

#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5;

int n,m;
int a[maxn+5];

long long sum[maxn+5];

int main(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    sort(a+1,a+n+1);
    long long ans;
    for (int i=1;i<=n;i++){
        sum[i]=sum[i-1]+a[i],ans=0;
        for (int j=i;j>0;j-=m){
            if (j>=m)
                ans+=(long long)((i-j)/m+1)*(sum[j]-sum[j-m]);
            else
                ans+=(long long)((i-j)/m+1)*(sum[j]);
        }
        printf("%lld ",ans);
    }
    return 0;
} 
View Code

  

  来考虑优化。很明显,因为对于每个$k$,它的甜度前$1~m$名、前$m+1~2m$名划分区间都是大不一样的,随着$k$的右移,前$1~m$名的划分区间也会右移,并不好下手。但是我们可以发现规律:$k$和$k+m$,它的甜度划分区间是类似的,即$k$的甜度前$1~m$名,是$k+m$的前$m+1~2m$。

  也就是说,我们在已知$k$的答案后,再多选择$m$个糖果时,相当于前面的$k$个糖果都推迟一天吃。我们设$d[i]$为吃$i$个糖果的答案,前缀和$sum[i]$为排序后前$i$个糖果的甜度之和,可以写出递推方程:

$$d[i+m]=d[i]+sum[i]+(sum[i+m]-sum[i])$$

  其中,第一个$sum[i]$是前面的$k$个糖果都推迟一天吃所造成的甜度上升,$(sum[i+m]-sum[i])$是多选择的$m$个糖果时(它们显然都在第一天吃)的甜度和。

  化简完就是:

$$d[i+m]=d[i]+sum[i+m]$$

  这样就用DP优化完啦!

  最终代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5;

int n,m;
int a[maxn+5];

long long sum[maxn+5];
long long d[maxn+5];

int main(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    sort(a+1,a+n+1);
    for (int i=1;i<=n;i++){
        sum[i]=sum[i-1]+a[i];
        d[i]=(i>=m)?d[i-m]+sum[i]:sum[i];
        printf("%lld ",d[i]);
    }
    return 0;
} 

 

posted @ 2022-12-29 22:48  wegret  阅读(48)  评论(0)    收藏  举报