懒惰的奶牛

【usaco2013 mar】lazy_bronze

题目描述
夏天又到了,奶牛贝里斯开始变得非常懒惰。他想要站在一个地方,然后只走很少的一段路,就能吃到尽可能多的美味的青草。有N块草坪排列在一条直线上,第i个草坪拥有g_i数量的青草,第i个草坪所在的位置是x_i。奶牛贝里斯想要在直线上选择一个点作为他的初始点(初始点有可能和草坪的位置重合),这样他就能吃到以这个点为中点距离不超过K的位置上的所有青草。如果初始点可以自由选择的话,请帮助贝里斯计算他最多能吃到的青草的数量。

输入
第一行是两个正整数,表示N和K。
第2行到第N+1行,每行两个整数,第i行的两个整数表示第i个草坪的g_i和x_i。

输出
输出贝里斯最多能吃到的青草数量。

样例输入
4 3
4 7
10 15
2 2
5 1

样例输出
11

数据范围限制
1<=N<=100000,1<=g_i<=10000,0<=x_i<=1000000,1<=k<=2000000。

提示
如果贝里斯将初始点选择在x=4的位置,那么他可以吃到x=1,x=2和x=7这三个地方的青草,总共是11。

题目大意
输入n组数,给出每一组数的位置x[i]和每组数的数值g[i]。求从任意某点开始不出k能收集到的最大的数值。
题目解析(两种解法)
1.(暴力)我们为了节省时间复杂度,可以开一个前缀和数组pre[i]。然后开始枚举每一个点前面2k范围以内可以收集到的数(所以下面的循环是倒着来的),然后直接暴力做出最大值就好了。(我这个程序还是90分……但大家也是可以学思路的)
这里说一下这个前缀和求一段值,我们可以这样求pre[i]-pre[i-k*2-1],重点:要-1。

#include<cstdio>
using namespace std;
long long n,k,g[100005],x[100005],f[1000005],m=1000005,pre[1000005],ans=-1;
long long max(long long a,long long b)
{
	if(a>b) return a;
	else return b;
}
void putin()
{
	scanf("%lld%lld",&n,&k);
	for(long long i=1;i<=n;++i)
	{
		scanf("%lld%lld",&g[i],&x[i]);
		f[x[i]]=g[i];
	}
	for(long long i=1;i<=m;++i)
		pre[i]=pre[i-1]+f[i];
}
int main()
{
	freopen("lazy_bronze.in","r",stdin);
	freopen("lazy_bronze.out","w",stdout);
	putin();
	for(long long i=m;i>=k*2+1;--i)
		ans=max(ans,pre[i]-pre[i-k*2-1]);
	printf("%lld",ans);
	return 0;
}

2.(贪心,求学老曾zhy_Learn的思路程序都是他的)求最大值,那贪心是可以考虑的一种方法。我们可以将输入的数据快排跑一遍,这样无疑会大大减少后续的时间。

#include<bits/stdc++.h>
using namespace std;
struct node{
	int g,v;
}a[100005];
bool cmp(node x,node y){
	return x.v<y.v;
}
int n,k,l,r,ma,ans,u=1;
int main(){
	freopen("lazy_bronze.in","r",stdin);
	freopen("lazy_bronze.out","w",stdout);
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++){
		scanf("%d%d",&a[i].g,&a[i].v);
	}
	sort(a+1,a+1+n,cmp);
	l=a[1].v;
	for(int i=1;i<=n;i++){
		r=a[i].v;
		ma+=a[i].g;
		if(r-k*2>l){
			ans=max(ma-a[i].g,ans);
			while(r-k*2>l){
				ma-=a[u].g;
				u++;
				l=a[u].v;
			}
		}
	}
	ans=max(ma,ans);
	printf("%d",ans);
	fclose(stdin);
	fclose(stdout);
	return 0;
}//by zhy_Learn

这两种解法中,第一种会比第二种要快一点的,毕竟第二种没有用到前缀和优化。在这里,谢谢老曾的指导!好了,那这道题就这么完成了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值