1. 程式人生 > >今日頭條2018春招-演算法方向題解

今日頭條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%資料。因為題目說的是最小數,不小於最小數的數都可以納入這個區間裡~