题解 | 贪心思路

相邻的糖果

https://www.nowcoder.com/practice/5799a1a6fb104a63bda2614cdf64b09b

如果把每组m个元素看成一个分区,我们的分区示意图如下:

可以明显看到:每个m元素的分区,削减最后一个元素,可以让影响后续m个分区,使它们的负担变小,因此我们的贪心策略是:每次优先删除一个分区的最后几个元素,因为这样可以帮助后续几个分区的总和一起减小。

在实现上,维护一个变量s,用来表示当前窗口的值,每次需要删除时,直接暴力枚举,也能过。

因为每个区间的操作次数与k其实是正相关的,比方说,假设k非常小,但区间和很大,那第一个区间处理完后,第二个区间就几乎不需要怎么处理,理论上的时间复杂度是O(nm)的,但由于这个贪心思路,使得每次删除都使得不合法区间数量收敛的特别快

如果区间很小,那就代表m很小,每次暴力枚举的次数也少,时间复杂度接近O(n)

如果区间很大,极端情况下是x很小,但区间大的话,如果删除一个区间的最后一个元素,会产生m\times \min(a[i],x)的反应,具体证明应该是计算整个数组最多一共需要减多少,然后再计算每次删一个元素,平均能减多少,这样下来几乎能过。

不太懂这些,还望有大佬证明证明

代码:

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+7;
typedef long long ll;
int n,m,x;
ll a[N];
//优先删每个分组最后的
int main(){
    ios::sync_with_stdio(false),cin.tie(0);
    cin>>n>>m>>x;
    m=min(n,m);
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    ll s=0;
    ll ans=0;
    for(int i=1;i<=n;i++){ //枚举每个分组最后一个
        s+=a[i];
        if(i>=m && s>x){
            ll need=s-x;
            for(int j=i;j>=i-m+1 && need; j--){
                if(need>a[j]){ //如果超出了,就能减多少是多少
                    need-=a[j];
                    s-=a[j];
                    ans+=a[j];
                    a[j]=0;

                }
                else {
                    s-=need;
                    ans+=need;
                    a[j]-=need;
                    need=0;
                }
            }
        }
        if(i>=m)s-=a[i-m+1];
    }
    cout<<ans;
    return 0;
}

全部评论
时间复杂度我个人是这么分析的,长度为p的窗口恰有n-p个,对于每个窗口,至多访问p个元素,所以时间复杂度是O( (n-p) * p) =O(np-p^2),这个时间复杂度分析与LeetCode438题目的暴力解法是一样的,p越小的时候,虽然减的少,但倍数也少了,p越大的时候,减的也越大,n和p几乎同阶。
点赞 回复 分享
发布于 02-07 12:01 广东

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务