这题在做的时候果断不会=.=
做了一晚上和一上午。当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; }