资源限制
内存限制:256.0MB C/C++时间限制:1.0s Java时间限制:3.0s Python时间限制:5.0s
问题描述
JiaoShou在爱琳大陆的旅行完毕,即将回家,为了纪念这次旅行,他决定带回一些礼物给好朋友。
在走出了怪物森林以后,JiaoShou看到了排成一排的N个石子。
这些石子很漂亮,JiaoShou决定以此为礼物。
但是这N个石子被施加了一种特殊的魔法。
如果要取走石子,必须按照以下的规则去取。
每次必须取连续的2*K个石子,并且满足前K个石子的重量和小于等于S,后K个石子的重量和小于等于S。
由于时间紧迫,Jiaoshou只能取一次。
现在JiaoShou找到了聪明的你,问他最多可以带走多少个石子。输入格式
第一行两个整数N、S。
第二行N个整数,用空格隔开,表示每个石子的重量。输出格式
第一行输出一个数表示JiaoShou最多能取走多少个石子。
样列输入
8 3
1 1 1 1 1 1 1 1样列输出
6
样列解释
任意选择连续的6个1即可。
数据规模和约定
对于20%的数据:N<=1000
对于70%的数据:N<=100,000
对于100%的数据:N<=1000,000,S<=10^12,每个石子的重量小于等于10^9,且非负
思路:
这道题的数据量很多而且数据很大,两层循环就得爆掉了,所以得考虑优化到线性或者log才有可能成功,我本来的想法是用前缀和,这样可以减少区间加的运算,但是到后边求石子数时因为区间长度不定,所以还是要套上两层循环,于是爆了。
在网上搜索然后发现一个网友的思路很好,他设置了两个数组,一个l[],一个r[],其中l[i]为以i位置为基准,向前边能扩展的最多石子数,如1,2,3时,s=3时,显然1+2=3,则l[1](从0开始)为2,因为可扩展0,1位置的石子。同理,r[i]是以i位置为基准,向后边能扩展的最多石子数。利用双指针可以将求l和r数组的时间复杂度优化成O(n)。
最后一步,一层循环遍历,只要求出最大的2*min(l[i],r[i+1])即为所求,很好理解,左边与右边同时扩展,扩展的数的最小值才是整体的值。r的位置为i+1,不能为i,为i的话与l[i]就有数字重叠了,不合题意。
不过看题目,好像用二分也可以试试,但得加上前缀和优化。就是设置边界l,r,以mid位置为基准,前缀和相减判断l到mid和mid+1到r是否符合标准,不符合就将边界减小。具体我也没细想,可以参考网友的想法:http://t.csdnimg.cn/zvDuR
代码如下:
标准代码:
#include<bits/stdc++.h>
using namespace std;
long long a[1000010];
int l[1000010],r[1000010];
int main(){long long n,s;
ios::sync_with_stdio(false);//取消输入输出缓存,加快cin、cout运算时间,这是个极大的优化cin>>n>>s;for(int i=0;i<n;i++)cin>>a[i];//求离左边最符合的石子数 long long sum1=a[0];for(int i=0,j=0;j<n;){if(sum1>s){sum1-=a[i];i++;}else if(sum1<=s){l[j]=j-i+1;j++;sum1+=a[j];}}//求离右边最符合的石子数 long long sum2=a[n-1];for(int i=n-1,j=n-1;j>=0;){if(sum2>s){sum2-=a[i];i--;}else if(sum2<=s){r[j]=i-j+1;j--;sum2+=a[j];}}int maxNum=0;for(int i=0;i<n-1;i++){maxNum=max(maxNum,2*min(l[i],r[i+1]));}cout<<maxNum;return 0;
}
第一次做时的前缀和方法代码(有错):
#include<bits/stdc++.h>
using namespace std;
long long pre[1000001];
int a[1000001];
int main(){long long n,s;cin>>n>>s;for(int i=1;i<=n;i++){cin>>a[i];pre[i]=pre[i-1]+a[i];}for(int i=n/2;i>=0;i--){for(int j=i;j<n-i;j++){if(pre[j]<=s&&pre[i+j]-pre[j]<=s){cout<<i*2;return 0;}}}cout<<0<<endl;return 0;
}