PTA1085 Perfect Sequence (25分)

这是一个值得仔细学习的题目。

给定一个正整数数列,和正整数 p,设这个数列中的最大值是 M,最小值是 m,如果 M≤mp,则称这个数列是完美数列。

现在给定参数 p 和一些正整数,请你从中选择尽可能多的数构成一个完美数列。

首先分析题目,要找完美数列,很自然可以想到排序,升序数列。
然后呢,要找一个大一个小,很显然需要利用two pointers思想,也就是从两头往中间查,找到尾部第一个V【j】满足大于V【i】*P即可,然后j-i就是所求,转化成求j-i的最大值,这个地方如果数组长度太长,很显然暴力求解是不太可能的,必须要用到二分查找。
algorithm头文件下的upper_bound和lower_bound是写好的二分查找函数
原型如下:

upper_bound(begin,end,num) // 返回第一个大于m的数组下标(vector迭代器)找不到就返回数组长度N和迭代器V.end()
lower_bound(begin,end,num) // 与upper不同的是,这个返回第一个大于等于的元素下标或vector迭代器。

若寻找小于或小于等于,需要使用如下方法:

lower_bound( begin,end,num,greater</type/>() ):
从数组的begin位置到end-1位置二分查找第一个小于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

upper_bound( begin,end,num,greater</type/>()
从数组的begin位置到end-1位置二分查找第一个小于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

使用greater()需要引用头文件 functional
在这里插入图片描述使用二分查找写的代码如下:
需要注意的是,最后一个测试点运算会超过int,这也是令人意想不到的错误,在刷题中尤其需要注意,如相加相乘都要考虑到溢出问题。

#include<iostream>
#include<vector>
#include<algorithm>
#include<functional>
using namespace std;
int main() {
	int N, P, max = 0, tmp;
	
	scanf("%d%d", &N,&P);
	vector<int>V(N);
	for (int i = 0; i < N; i++) {
		scanf("%d", &V[i]);
	}
	sort(V.begin(), V.end());
	for (int i = 0; i < N; i++) {
		auto it = upper_bound(V.begin() + i, V.end(), (long long)V[i] * P);
		tmp = it - (V.begin() + i);
		if (tmp > max)max = tmp;
	}
	printf("%d", max);
	return 0;
}

第二个方法是一种高明的做法,具体有多高明一看就知道了。

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main() {
	int N, M, res = 0, tmp;
	scanf("%d %d", &N, &M);
	vector<int>V(N);
	for (int i = 0; i < N; i++) {
		scanf("%d", &V[i]);
	}
	sort(V.begin(), V.end());
	for (int i = 0; i < N; i++) {
		for (int j = i + res; j < N; j++) {
			if (V[j] <= (long long)V[i] * M) {
				tmp = j - i + 1;
				if (tmp > res)res = tmp;
			}
			else break;	//如果不加break,效率大打折扣。
		}
	}
	printf("%d", res);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值