POJ 2008 优先队列 好题 数形结合

这题在做的时候果断不会=.=

做了一晚上和一上午。当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;
}








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值