【(类似DP)优化】 分梨子

分梨子

时间限制: 1 Sec   内存限制: 64 MB
提交: 32   解决: 14
[ 提交][ 状态][ 我的提交]

题目描述

  Finley家的院子里有棵梨树,最近收获了许多梨子。于是,Finley决定挑出一些梨子,分给幼稚园的宝宝们。可是梨子大小味道都不太一样,一定要尽量挑选那些差不多的梨子分给孩子们,那些分到小梨子的宝宝才不会哭闹。   每个梨子都具有两个属性值,Ai和Bi,本别表示梨子的大小和甜度情况。假设在选出的梨子中,两个属性的最小值分别是A0和B0。只要对于所有被选出的梨子i,都满足C1*(Ai-A0)+C2*(Bi-B0)≤C3(其中,C1、C2和C3都是已知的常数),就可以认为这些梨子是相差不多的,可以用来分给小朋友们。   那么,作为幼稚园园长的你,能算出最多可以挑选出多少个梨子吗?

输入

第一行一个整数N(1≤N≤2000),表示梨子的总个数。 第二行三个正整数,依次为C1,C2和C3(C1,C2≤2000,C3≤10^9)。 接下来的N行,每行两个整数。第i行的两个整数依次为Ai和Bi。

输出

只有一个整数,表示最多可以选出的梨子个数。

样例输入

 (如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)

3
2 3 6
3 2
1 1
2 1

样例输出

2


分析:
    我们很容易想到枚举最低点再验证,但这是n^3的算法,明显会超时。
    那么我们就要想如何把时间复杂度降为n^2.
    首先看限制条件:C1*(Ai-A0)+C2*(Bi-B0)≤C3,为了方便验证,把它变形为:C1*Ai+C2*Bi-C3<=C1*A0+C2*B0
    不等式的前面部分仅与i号梨子有关,所以我们把每个梨子的此值存下来,记为d[i]。再把Bi存下来,记为c[i]。记下c,d的梨子编号
    将c,d排序后,枚举A0,在枚举B0,但是此时的B0换成了c[i],是有顺序的。
    基于这个顺序,我们可以得到优化:在c[i]从小到大递增的过程中,我们不必每次都把每个梨子验证一次,而可以借助已经算过的值。(详见代码)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node
{
	int x,id;
	bool operator < (const node& p)const{return x<p.x;}
}c[2005],d[2005];
int a[2005],b[2005],ans,cnt[2005];
int main()
{
	int n,c1,c2,c3;
	scanf("%d%d%d%d",&n,&c1,&c2,&c3);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&a[i],&b[i]);
		c[i].id=d[i].id=i;//存编号
		c[i].x=b[i];
		d[i].x=a[i]*c1+b[i]*c2-c3;
	}
	sort(c+1,c+1+n);
	sort(d+1,d+1+n);
	for(int i=1;i<=n;i++)//枚举A0
	{
		int k=0,sum=0;
		memset(cnt,0,sizeof cnt);//cnt用来记录该甜度被计算了几次
		for(int j=1;j<=n;j++)//枚举c[j](B0)
		{
			//因为c[j]递增,所以前面能够满足限制的d值在c[j]增加后仍然满足,所以不必再从1到n把d验证一遍,从而达到优化
			while(k<=n&&d[k].x<=a[i]*c1+c[j].x*c2)//如果d[k]满足限制
			{//这个while在j从1到n中只会执行n次,大大小于n^2
				if(a[d[k].id]>=a[i]&&b[d[k].id]>=c[j].x)//看d[k]原来的a,b是否大于等于最小值
				{
					sum++;//选一个梨子
					cnt[b[d[k].id]]++;//此b值算了一次
				}
				k++;
			}
			sum-=cnt[c[j-1].x];//减去b值已经小于最小值的梨子,因为c排过序,所以减去c[j-1]足矣
			cnt[c[j-1].x]=0;//如果c[j]==c[j-1]可能会重复减
			ans=max(ans,sum);//保存可以选的最大梨子数
		}
	}
	printf("%d",ans);
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值