很明显,贪心策略是先吃甜度大的可以保证最终的总甜度最小,因此我们先从小到大排个序。一天可以吃$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; }
标签:Eating,int,sum,Codeforces,long,甜度,Sweets,maxn,糖果 From: https://www.cnblogs.com/wegret/p/17013743.html