题目
食堂有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个食材且
则这道菜的美味程度可以用表示
也就是要求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个变量