今日头条2018春招-算法方向题解

1.P为给定的二维平面整数点集。定义 P 中某点x,如果x满足 P 中任意点都不在 x 的右上方区域内(横纵坐标都大于x),则称其为“最大的”。求出所有“最大的”点的集合。(所有点的横坐标和纵坐标都不重复, 坐标轴范围在[0, 1e9) 内)

如下图:实心点为满足条件的点的集合。请实现代码找到集合 P 中的所有 ”最大“ 点的集合并输出。

第一行输入点集的个数 N, 接下来 N 行,每行两个数字代表点的 X 轴和 Y 轴。
对于 50%的数据,  1 <= N <= 10000;
对于 100%的数据, 1 <= N <= 500000;
输出“最大的” 点集合, 按照 X 轴从小到大的方式输出,每行两个数字分别代表点的 X 轴和 Y轴。

输入例子1:

5
1 2
5 3
4 6
7 5
9 0

输出例子1:

4 6
7 5
9 0

对于50万的数据,暴力求解On2肯定超时,连1W的数据都超时,于是选择温柔求解~

先对所有点的y坐标从大到小排序。对于y坐标最大的点,它一定是“最大”点,对于x坐标第二大的点,它必须比第一个点的x坐标大,才能是“最大”点。以此类推,对于y坐标第n大的点,它的x坐标必须比前面的点都大。(“前面”指y坐标第1~n-1大的点)

如果排好序之后每个数都往前比一遍y坐标,复杂度0.5n^2,依旧超时。于是想到设一个maxx,表示前面的数最大的x坐标,每次就只用和maxx比一次x坐标,不用循环比较。复杂度就是On。不过因为排序复杂度是nlogn,所以整体复杂度nlogn~

上代码……

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
struct nn{
	int x,y;
}qq[500005];
bool cmp(nn a,nn b){
	return a.y>b.y;
}
int main(){
	int n,ans=0,maxx=-1;
	cin>>n;
	for(int i=0;i<n;i++)
		scanf("%d%d",&qq[i].x,&qq[i].y);
	sort(qq,qq+n,cmp);//对y坐标比较
	for(int i=0;i<n;i++)
		if(qq[i].x>maxx){
			printf("%d %d\n",qq[i].x,qq[i].y);
			maxx=qq[i].x;//更新最大的y坐标
		}
}

二十几行就搞定了,舒服~

题目要求按x坐标从小到大输出,所以这里排序是对y从大到小排序,因为只要它是“最大”点,y最大时x就最小。所以我可以直接输出啦~啦啦啦~

2.给定一个数组序列, 需要求选出一个区间, 使得该区间是所有区间中经过如下计算的值最大的一个:

区间中的最小数 * 区间所有数的和最后程序输出经过计算后的最大值即可,不需要输出具体的区间。如给定序列  [6 2 1]则根据上述公式, 可得到所有可以选定各个区间的计算值:

[6] = 6 * 6 = 36;

[2] = 2 * 2 = 4;

[1] = 1 * 1 = 1;

[6,2] = 2 * 8 = 16;

[2,1] = 1 * 3 = 3;

[6, 2, 1] = 1 * 9 = 9;

从上述计算可见选定区间 [6] ,计算值为 36, 则程序输出为 36。

区间内的所有数字都在[0, 100]的范围内;

输入描述:

第一行输入数组序列长度n,第二行输入数组序列。
对于 50%的数据,  1 <= n <= 10000;
对于 100%的数据, 1 <= n <= 500000

输出描述:

输出数组经过计算后的最大值。

输入例子1:

3
6 2 1

输出例子1:

36

又是50万的数据!这些序列的排列组合方式肯定算不过来了,暴力无解。而且这道题是不能排序的,因为求的是最大区间。我一开始想错了,直接排序,GG。

于是想到对于每一个数往左找,往右找,找到比他小的数为止。左边界和右边界形成一个区间,就只会列出50万个区间。区间再乘这个数,就是对于这个数来说的最大区间,最后比较每一个数的最大区间,得到答案。

但是如果题目给出极端数据,1、2、3、4……500000这种,每一个数往右找区间边界都要找到底,时间复杂度直接爆炸。虽然本道题说数字给定1-100的整数,没有小数,也就是说区间的长度不会那么长,这样做肯定不会超时,但我对自己要求高一点~我不能这样比到底。于是我给上述算法融合了动态规划~

上代码……

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int a[500002],sum[500002],r[500002],l[500002];
int main(){
	int n;
	cin>>n; 
	memset(sum,0,sizeof sum);
	for(int i=1;i<=n;i++){
		cin>>a[i];
		sum[i]=sum[i-1]+a[i];
	}
	for(int i=1;i<=n;i++){//每一个点往左遍历,直到遇到比自己小的 
		int temp=i;
		while(temp>1&&a[i]<=a[temp-1])//这里是<= 
                    temp=l[temp-1];//DP思想 
		l[i]=temp;
	}
	for(int i=n;i>=1;i--){//每一个点往右遍历,直到遇到比自己小的 
		int temp=i;
		while(temp<n&&a[i]<=a[temp+1])    temp=r[temp+1];
		r[i]=temp;
	}
	int ans;
	for(int i=1;i<=n;i++)
	    ans=max(ans,(sum[r[i]]-sum[l[i]-1])*a[i]);
	cout<<ans;
}

从最左端开始循环,每一个数往左比,如果第二个数比第一个数小,DP思想那一行,直接就等于l【temp-1】,就不用一直比到底了。同理从最右端开始循环,往右比,对于1-500000的极端数据复杂度就为On啦~

那个小于等于要注意,我一开始写的小于,只过了70%数据。因为题目说的是最小数,不小于最小数的数都可以纳入这个区间里~

猜你喜欢

转载自blog.csdn.net/qq_41643650/article/details/79630796
今日推荐