奇怪的厨师 (RMQ+前缀和)

这是一篇关于如何利用RMQ(Range Minimum Query)和前缀和解决一道食堂厨师烹饪问题的博客。厨师需要使用连续食材制作不同菜品,目标是最大化M道菜的美味度之和。文章探讨了从DFS到优化的解决方案,最终提出通过枚举和大根堆实现高效算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

食堂有N种食材,编号从1到N,每种食材有自己的美味度Di。食堂的厨师很奇怪,他只会用编号连续的食材做菜,并且一道菜包含的食材种类数至少是L种,但又不能超过R种。一道菜的美味度等于它包含的所有种类食材的美味度之和。如果两道菜包含的食材种类完全一致,那么就被认为是相同的。

现在厨师要准备M道不同的菜,你能告诉他这M道菜的美味度之和最大是多少吗?

输入格式

第1行包括四个正整数N,M,L,R。其中N是食材的种数,M是要准备的菜的数量,L和R是一道菜组成的食材种类数的上限和下限。

接下来N行每行包括一个整数Di,其中第i行的Di表示第i种食材的美味度。

输出格式

一个整数表示M道不同的菜最大的美味度之和。

样例

样例输入

4 5 1 3
1
3
-1
-2

样例输出

13

数据范围与提示

1≤L≤R≤N≤500000,1≤M≤500000,-1000≤Di≤1000。

具体思路:

由于是连续的,可以想到用前缀和差分来做(用sum数组)

那么,这个问题就很容易转换成RMQ了

但是,我只是知道一个菜有多少个食材,况且还是个范围,这怎么求呢?

这时,可以先假设我已经知道了第k道菜的第1个食材的编号为i,且一直做到第i+k个食材且i+l-1<=i+k<=i+r-1

则这道菜的美味程度可以用sum[i+k]-sum[i-1]表示

也就是要求sum[i+k]最大,由于k有范围

现在k是可以用RMQ得到的,那么i怎么求到呢?

所以在考场上,我也不知道自己怎么了写了dfs。。

#include<iostream>
#include<cstdio>
#include<vector>
#include <stack>
#include <cmath>
#include <cstring>
using namespace std;
#define ll long long
const int MAXN = 500003;
int n ,  m;
ll sum[MAXN];
ll ans;
int l , r;
void read( int &x ){
    x = 0;char s = getchar();
    int f = 1;
    while( s < '0' || s > '9' ){
        if( s == '-' )
            f = -1;
        s = getchar();
    }
    while( s >= '0' && s <= '9' ){
        x = x * 10 + s -'0';
        s = getchar();
    }
     x *= f;
}
void dfs( int x , int now , ll s , int p ){
    if( now == m ){
        ans = max( ans , s );
        return ;
    }
    if( x + p - 1 > n ) return ;
    int k = 0;
    for( int i = x + p - 1 ; i <= x + r - 1 ; i ++ ){
        k ++;
        dfs( x + 1 , now + 1 , s + sum[i] - sum[x-1] , l );
        dfs( x , now + 1 , s + sum[i] - sum[x-1] , p + k );
    }
    for( int i = x + 1 ; i <= n ; i ++ ){
        int k = 0;
        dfs( i , now , s  , l );
    }
}
int main(){
    read( n );read( m );read( l );read( r );
    ans = -0x7f7f7f7f;
    for( int i = 1 ; i <= n ; i ++ ){
        int x;
        read( x );
        sum[i] = sum[i-1] + x;
    }
    dfs( 1 , 0 , 0 , l );
    printf( "%lld" , ans );
    return 0;
}

可见自己的思维深度不够,还不会熟练地运用RMQ

考后才发现可以不用dfs,直接枚举所有情况,把它们放进一个大根堆里就可以了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <algorithm>
#include <algorithm>
#include <climits>
#include <queue>
#define ll long long
using namespace std;
const int MAXN = 500003;
int n , m , l , r;
ll sum[MAXN];
int x;
priority_queue<ll>q;
int main(){
    scanf( "%d%d%d%d" , &n , &m , &l , &r );
    for( int i = 1 ; i <= n ; i ++ ){
        int x;
        scanf( "%d" , &x );
        sum[i] = sum[i-1] + x;
    }
    for( int i = 1 ; i <= n ; i ++ ){
        if( i + l - 1 > n )
            continue;
        for( int j = i + l - 1; j <= min( i + r - 1 , n ); j ++ )
            q.push( sum[j] - sum[i-1] );
    }
    ll ans = 0;
    for( int i = 1 ; i <= m; i ++ ){
        ans += q.top();
        q.pop();
    }
    printf( "%lld" , ans );
    return 0;
}

现在回到上面需解决的问题,其实解决它也很简单,既然我不知道,那我把所有情况枚举出来不就行了

采用第二种的暴搜方法

我们定义一个结构体

从第i个食材为第一个食材,最后一个食材的下标的范围是fl到fr的一种菜

这样我们是可以求的吧

假设现在最后一个食材的下标为k时,这个菜最美味(美味度比不为k的所有情况都高)

但是并不代表这个结构体就作废了

那样例来说:

4 5 1 3
1
3
-1
-2

我们不仅要选{1,3},还要选{1,3,-1}才有可能得出13来

那么怎么求以第i个食材为第一个食材的其它情况呢,因为如果不改变fl与fr由于rmq的区间会包括,所以它仍会求到k

所以就将它分成两个部分

1.以第i个食材为第一个食材,最后一个食材的下标的范围是fl到k-1

2.第i个食材为第一个食材,最后一个食材的下标的范围是k+1到fr

把它们进堆

当然,如果左端点比右端点还大,就不进堆了

这里要抓住一个重点:如果这个菜的第一个食材位置确定,那么我一定可以让这个菜的美味度最大化

最后一个问题:

堆里最开始放什么?

前面只是简单说了一下

其实就是放n个结构体,每个结构体有:以第i个食材为第一道菜,最后一个食材的下标的范围为fl到fr,且当最后一个食材下标为k时这道菜最美味     4个变量

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值