P1493 分梨子
P1493 分梨子
题目描述
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
说明
各个测试点2s
样例说明:可以选择1、3两个梨子或者2、3两个梨子。
分析:
反正蒟蒻没推出dp式子,但是把不等式化一化,然后弄个排序暴力模拟均摊N^2的效率跑过去了。
简单的讲一件怎么搞吧。
首先根据题意:c1*(ai-a0)+c2*(bi-b0)<=c3
---->c1*ai+c2*bi-c3<=c1*a0+c2*b0
显然,不等式的左边是关于i的一个常数,弄个数组d先保存好。然后我们来看看怎么处理右边。
我们常规的思路就是:枚举a0,b0(n^2),然后再暴力统计一下(n),总的O(n^3),我们借鉴一下单调队列优化dp的思路,考虑直接枚举a0,但是把b0排序好,然后按某种方式统计,看看能否提高效率。
先从理论上分析是否有提高的可能性:首先d数组必须要排序,由于d数组的单调不下降性,所以b0枚举如果是有序的,那么是可以节省一些时间的!
再来仔细分析一下怎么来优化:我们先一层循环枚举a0,然后一层循环枚举b0,然后一层循环在d数组统计答案。
显然,如果我们枚举到一个a0[i],b0[j],当枚举到d[k]时,不等式不成立了,那么另一句话就是从1...k-1不等式都是成立的,那么我们计算b0[j+1]的结果时,只有在原b数组中1....k-1中大于b0[j+1]的数字是不合法的,然后直接从k到n继续判断d[k]是否能使不等式成立就行了,
这时就是一个类似单调队列优化dp的过程了!
先给一段伪代码:
for i=1 to n
for j=1 to n
for k=k to n
第三层循环的k是不下降的,所以最多只会在j的循环下枚举n次(均摊意义),所以总的就是O(n*n)的了!
那么如何维护一下1....k-1中大于某个数的数字个数呢?
想到逆序对没有?对呀,树状数组不就好了?
不过更巧妙的,由于我们这里有了一层循环,并且b0的枚举是有序的,我们直接开个桶,累计每个数字使用的次数,然后累加时把小于当前数字桶中的全减掉,然后把那个数字的桶清空,防止重复减就行了!
当然,想不到桶的做法也是没有关系的,毕竟n只有2000,用树状数组logn=11,那么就是2000*2000*11=4.4*10^7。还是可以过的哟
(^U^)ノ~YO
参考代码:
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 const int N=2010; 6 struct Pear{ 7 int v,idx; 8 bool operator < (const Pear &rhs) const{ 9 return v<rhs.v; 10 } 11 Pear(int v=0,int idx=0):v(v),idx(idx){} 12 }c[N],d[N]; 13 int a[N],b[N],sum[N]; 14 int n,c1,c2,c3; 15 int main(){ 16 scanf("%d%d%d%d",&n,&c1,&c2,&c3); 17 //c1*(ai-a0)+c2*(bi-b0)<=c3 18 //c1*ai+c2*bi-c3<=c1*a0+c2*b0 19 for (int i=1;i<=n;i++){ 20 scanf("%d%d",&a[i],&b[i]); 21 c[i]=Pear(b[i],i); 22 d[i]=Pear(a[i]*c1+b[i]*c2-c3,i); 23 } 24 sort(c+1,c+n+1);sort(d+1,d+n+1); 25 int res=0; 26 for (int i=1,ans=0;i<=n;i++,ans=0){ 27 memset(sum,0,sizeof(sum)); 28 for (int j=1,k=0;j<=n;j++){ 29 for (;k<=n && d[k].v<=c1*a[i]+c2*c[j].v;k++){ 30 if (a[d[k].idx]>=a[i] && b[d[k].idx]>=c[j].v){ 31 ans++; 32 sum[b[d[k].idx]]++; 33 } 34 } 35 ans-=sum[c[j-1].v]; 36 sum[c[j-1].v]=0; 37 res=max(res,ans); 38 } 39 } 40 printf("%d",res); 41 return 0; 42 }