这题在做的时候果断不会=.=
做了一晚上和一上午。当AC之后,感觉很爽!
给定一个集合,集合中的元素有w,h两个属性。在此集合中取出一个子集,满足下列条件:
A*(H-h)+B*(W-w)<=C;
其中h,w为取出子集的所有元素中最小的h和w.
求子集中最多元素个数。
朴素的办法为:
枚举 w和h,将符合条件的点计数,这样O(n^3)的算法过不掉...
分析:
1.不等式A*(H-h)+B*(W-w)<=C;将w,h替换为x,y就满足了二元一次方程的线性规划特征。
2.在子集中的所有点都满足H>h,W>w.且A*(H-h)+B*(W-w)<=C。这就是一个以(w,h)为直角顶点的直角三角形。
两直角边长分别为C/A.C/A.于是,题目转变成在平面点集中,用这样一个三角形框框框住的最多点数。
优化朴素算法:
枚举一个w值,h单调时,斜边也单调。
那么按h递减的顺序排序,保证优先队列中的规则为斜边递减排序。
形象的说,就是以水平直角边逐渐下移,优先队列维护点集。
计算最大值即可。
数形结合啊!讲白了,这题很简单,我怎么做了这么久,思维严谨!
#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
struct node
{
int w,h,v;
friend bool operator<( node a,node b ){
return a.v<b.v;
}
}sh[1111];
bool cmp_h( node a,node b ){
return a.h>b.h;
}
int main()
{
int n,a,b,c;
while( scanf("%d %d %d %d",&n,&a,&b,&c)!=EOF )
{
for( int i=0;i<n;i++ )
{
scanf( "%d %d",&sh[i].h,&sh[i].w );
sh[i].v=a*sh[i].h+b*sh[i].w;
}
priority_queue<node> PQ;
sort( sh,sh+n,cmp_h );
int ans=0;
for( int i=0;i<n;i++ )
{
while( !PQ.empty() ) PQ.pop();
int min_H=sh[i].h;
int min_W=sh[i].w;
for( int j=0;j<n;j++ )
{
if( sh[i].v>min_H*a+min_W*b+c )
break;
if( sh[j].v<=min_H*a+min_W*b+c )
{
min_H=min(sh[j].h,min_H);
if( sh[j].w>=min_W )
PQ.push(sh[j]);
while( !PQ.empty() && PQ.top().v>min_H*a+min_W*b+c )
PQ.pop();
}
ans=max(ans,(int)PQ.size());
}
}
printf( "%d\n",ans );
}
return 0;
}